shiro + mybatis+ spring (只用shiro的密码校验和并发剔除)——不用权限之类
2016-05-20 09:13
615 查看
</pre> shiro很强大,但往往项目不可能大改造,往往只需要部分功能,比如用到验证码,加密,还有就是同一个账户在两个地方登录,剔除第一个登录者,本文只提供思路和部分代码,<p></p><p></p><p>自定义实现ream,</p><p></p><pre name="code" class="java">package com.shiro.shiro.realm; import java.util.HashSet; import java.util.Set; import org.apache.shiro.authc.*; import org.apache.shiro.authz.AuthorizationInfo; import org.apache.shiro.authz.SimpleAuthorizationInfo; import org.apache.shiro.realm.AuthorizingRealm; import org.apache.shiro.subject.PrincipalCollection; import org.apache.shiro.util.ByteSource; import org.springframework.beans.factory.annotation.Autowired; import com.shiro.model.User; import com.shiro.service.UserService; public class UserRealm extends AuthorizingRealm { @Autowired private UserService userService; //获取授权信息 @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); <span style="font-size:18px;"> <span style="color:#ff0000;"><strong>Set<String> set =new HashSet<String>(); set.add("*:*:*");</strong></span> <span style="color:#ff0000;"><strong>authorizationInfo.setRoles(set); authorizationInfo.setStringPermissions(set); return authorizationInfo;</strong></span></span> } //获取身份验证信息 @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { String username = (String)token.getPrincipal(); User user = userService.findByUsername(username); if(user == null) { throw new UnknownAccountException();//没找到帐号 } if(Boolean.TRUE.equals(user.getLocked())) { throw new LockedAccountException(); //帐号锁定 } //交给AuthenticatingRealm使用CredentialsMatcher进行密码匹配,如果觉得人家的不好可以自定义实现 SimpleAuthenticationInfo authenticationInfo = new SimpleAuthenticationInfo( user.getUsername(), //用户名 user.getPassword(), //密码 ByteSource.Util.bytes(user.getCredentialsSalt()),//salt=username+salt getName() //realm name ); return authenticationInfo; } @Override public void clearCachedAuthorizationInfo(PrincipalCollection principals) { super.clearCachedAuthorizationInfo(principals); } @Override public void clearCachedAuthenticationInfo(PrincipalCollection principals) { super.clearCachedAuthenticationInfo(principals); } @Override public void clearCache(PrincipalCollection principals) { super.clearCache(principals); } public void clearAllCachedAuthorizationInfo() { getAuthorizationCache().clear(); } public void clearAllCachedAuthenticationInfo() { getAuthenticationCache().clear(); } public void clearAllCache() { clearAllCachedAuthenticationInfo(); clearAllCachedAuthorizationInfo(); } }
只要在<pre name="code" class="java">doGetAuthorizationInfo方法体里授权所有资源所有角色就好了
</pre><pre name="code" class="java">
剔除功能
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.shiro.filter; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import org.apache.shiro.session.Session; import org.apache.shiro.session.mgt.DefaultSessionKey; import org.apache.shiro.session.mgt.SessionManager; import org.apache.shiro.subject.Subject; import org.apache.shiro.web.filter.AccessControlFilter; import org.apache.shiro.web.util.WebUtils; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import java.io.Serializable; import java.util.Deque; import java.util.LinkedList; /** * 并发剔除 */ public class KickoutSessionControlFilter extends AccessControlFilter { private String kickoutUrl; //踢出后到的地址 private boolean kickoutAfter = false; //踢出之前登录的/之后登录的用户 默认踢出之前登录的用户 private int maxSession = 1; //同一个帐号最大会话数 默认1 private SessionManager sessionManager; private Cache<String, Deque<Serializable>> cache; public void setKickoutUrl(String kickoutUrl) { this.kickoutUrl = kickoutUrl; } public void setKickoutAfter(boolean kickoutAfter) { this.kickoutAfter = kickoutAfter; } public void setMaxSession(int maxSession) { this.maxSession = maxSession; } public void setSessionManager(SessionManager sessionManager) { this.sessionManager = sessionManager; } public void setCacheManager(CacheManager cacheManager) { this.cache = cacheManager.getCache("shiro-kickout-session"); } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { return false; } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { Subject subject = getSubject(request, response); if(!subject.isAuthenticated() && !subject.isRemembered()) {//未授权且非remember //如果没有登录,直接进行之后的流程 return true; } Session session = subject.getSession(); String username = (String) subject.getPrincipal(); Serializable sessionId = session.getId(); //TODO 同步控制 Deque<Serializable> deque = cache.get(username); if(deque == null) { deque = new LinkedList<Serializable>(); cache.put(username, deque); } //如果队列里没有此sessionId,且用户没有被踢出;放入队列 if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) { deque.push(sessionId); } //如果队列里的sessionId数超出最大会话数,开始踢人 while(deque.size() > maxSession) { Serializable kickoutSessionId = null; if(kickoutAfter) { //如果踢出后者 kickoutSessionId = deque.removeFirst(); } else { //否则踢出前者 kickoutSessionId = deque.removeLast(); } try { Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId)); if(kickoutSession != null) { //设置会话的kickout属性表示踢出了 kickoutSession.setAttribute("kickout", true); } } catch (Exception e) {//ignore exception } } //如果被踢出了,直接退出,重定向到踢出后的地址 if (session.getAttribute("kickout") != null) { //会话被踢出了 try { subject.logout();//这里走的shiro的退出 } catch (Exception e) { //ignore } saveRequest(request); WebUtils.issueRedirect(request, response, kickoutUrl); return false; } return true; } }
此处用到了缓存,当然可以自己实现,把session存到数据库中然后判断操作
</pre><pre name="code" class="java">再看下部分配置文件
</pre><pre name="code" class="java">
<pre name="code" class="html"><?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:util="http://www.springframework.org/schema/util" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- 缓存管理器 --> <bean id="cacheManager" class="com.shiro.spring.cache.SpringCacheManagerWrapper"> <property name="cacheManager" ref="springCacheManager"/> </bean> <!-- 凭证匹配器 --> <bean id="credentialsMatcher" class="com.shiro.shiro.credentials.CredentialsMatcher"> <property name="hashAlgorithmName" value="md5"/> <property name="hashIterations" value="2"/> <property name="storedCredentialsHexEncoded" value="true"/> </bean> <!-- Realm实现 --> <bean id="userRealm" class="com.shiro.shiro.realm.UserRealm"> <property name="credentialsMatcher" ref="credentialsMatcher"/> <property name="cachingEnabled" value="false"/> </bean> <!-- 会话ID生成器 --> <bean id="sessionIdGenerator" class="org.apache.shiro.session.mgt.eis.JavaUuidSessionIdGenerator"/> <!-- 会话Cookie模板 maxAge=-1表示浏览器关闭时失效此Cookie--> <bean id="sessionIdCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="sid"/> <property name="httpOnly" value="true"/> <property name="maxAge" value="-1"/> </bean> <!-- 会话DAO --> <bean id="sessionDAO" class="org.apache.shiro.session.mgt.eis.EnterpriseCacheSessionDAO"> <property name="activeSessionsCacheName" value="shiro-activeSessionCache"/> <property name="sessionIdGenerator" ref="sessionIdGenerator"/> </bean> <!-- 会话验证调度器 --> <bean id="sessionValidationScheduler" class="org.apache.shiro.session.mgt.quartz.QuartzSessionValidationScheduler"> <property name="sessionValidationInterval" value="1800000"/> <property name="sessionManager" ref="sessionManager"/> </bean> <!-- 会话管理器 --> <bean id="sessionManager" class="org.apache.shiro.web.session.mgt.DefaultWebSessionManager"> <property name="globalSessionTimeout" value="1800000"/> <property name="deleteInvalidSessions" value="true"/> <property name="sessionValidationSchedulerEnabled" value="true"/> <property name="sessionValidationScheduler" ref="sessionValidationScheduler"/> <property name="sessionDAO" ref="sessionDAO"/> <property name="sessionIdCookieEnabled" value="true"/> <property name="sessionIdCookie" ref="sessionIdCookie"/> </bean> <!-- 安全管理器 --> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <property name="realm" ref="userRealm"/> <property name="sessionManager" ref="sessionManager"/> <property name="cacheManager" ref="cacheManager"/> </bean> <!-- 相当于调用SecurityUtils.setSecurityManager(securityManager) --> <bean class="org.springframework.beans.factory.config.MethodInvokingFactoryBean"> <property name="staticMethod" value="org.apache.shiro.SecurityUtils.setSecurityManager"/> <property name="arguments" ref="securityManager"/> </bean> <!-- 基于Form表单的身份验证过滤器 --> <strong> <bean id="authcFilter" class="com.shiro.shiro.filter.MyFormAuthenticationFilter"> <property name="usernameParam" value="username"/> <property name="passwordParam" value="password"/> <property name="failureKeyAttribute" value="shiroLoginFailure"/> </bean> <bean id="validateFilter" class="com.shiro.shiro.filter.ValidateFilter"> <property name="verificationAbled" value="true"/> <property name="verificationParam" value="verificationParam"/> <property name="failureKeyAttribute" value="shiroLoginFailure"/> </bean></strong> <!-- Shiro的Web过滤器 --> <bean id="shiroFilter" class="org.apache.shiro.spring.web.ShiroFilterFactoryBean"> <property name="securityManager" ref="securityManager"/> <property name="loginUrl" value="/login"/> <property name="filters"> <util:map> <entry key="authc" value-ref="<strong>authcFilter</strong>"/> <entry key="sysUser" value-ref="sysUserFilter"/> <entry key="validateFilter" value-ref="<strong>validateFilter</strong>"/> <entry key="kickout" value-ref="<strong>kickoutSessionControlFilter</strong>"/> </util:map> </property> <property name="filterChainDefinitions"> <value> /login = <strong>validateFilter</strong>,authc /logout = logout /authenticated = authc /** = <strong>kickout</strong>,user,sysUser </value> </property> </bean> <!-- currenuser --> <bean id="sysUserFilter" class="com.shiro.shiro.filter.SysUserFilter"/> <!-- 并发踢出 kickoutAfter:是否踢出后来登录的,默认是false kickoutUrl:被踢出后重定向到的地址 maxSession:同一个用户最大的会话数,默认1 --> <bean id="kickoutSessionControlFilter" class="com.shiro.shiro.filter.KickoutSessionControlFilter"> <property name="cacheManager" ref="cacheManager"/> <property name="sessionManager" ref="sessionManager"/> <property name="kickoutAfter" value="false"/> <property name="maxSession" value="2"/> <property name="kickoutUrl" value="/login?kickout=1"/> </bean> <!-- Shiro生命周期处理器--> <bean id="lifecycleBeanPostProcessor" class="org.apache.shiro.spring.LifecycleBeanPostProcessor"/> </beans>
</pre><pre name="code" class="java">自定义的
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.shiro.filter; import org.apache.shiro.web.filter.authc.FormAuthenticationFilter; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; /** * 自定义shiro FormAuthenticationFilter 表单过滤器 * @author changliang * */ public class MyFormAuthenticationFilter extends FormAuthenticationFilter { /** * 授权是否失败 */ @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { if(request.getAttribute(getFailureKeyAttribute()) != null) { return true; } return super.onAccessDenied(request, response, mappedValue); } }
</pre>密码校验<pre>
<pre name="code" class="java">package com.shiro.shiro.filter; import org.apache.shiro.web.filter.AccessControlFilter; import org.apache.shiro.web.util.WebUtils; import org.springframework.beans.factory.annotation.Autowired; import com.shiro.service.UserService; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; /** * 验证码过滤器 */ public class ValidateFilter extends AccessControlFilter { @SuppressWarnings("unused") @Autowired private UserService userService; private boolean verificationAbled = true; //是否开启验证码支持 @SuppressWarnings("unused") private String verificationParam = "verificationParam"; //前台提交的验证码参数名 private String failureKeyAttribute = "shiroLoginFailure"; //验证码验证失败后存储到的属性名 public void setVerificationAbled(boolean verificationAbled) { this.verificationAbled = verificationAbled; } public void setVerificationParam(String verificationParam) { this.verificationParam = verificationParam; } public void setFailureKeyAttribute(String failureKeyAttribute) { this.failureKeyAttribute = failureKeyAttribute; } @Override protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception { //1、设置验证码是否开启属性,页面可以根据该属性来决定是否显示验证码 request.setAttribute("verificationAbled", verificationAbled); HttpServletRequest httpServletRequest = WebUtils.toHttp(request); //2、判断验证码是否禁用 或不是表单提交(允许访问) if (verificationAbled == false || !"post".equalsIgnoreCase(httpServletRequest.getMethod())) { return true; } <span style="color:#ff0000;"><strong> //3、此时是表单提交,验证验证码是否正确 //TODO 增加自己的验证码校验 //return userService.verification(verificationParam); return true;</strong></span> } @Override protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception { //如果验证码失败了,存储失败key属性 //在LoginController里过滤用到 request.setAttribute(failureKeyAttribute, "<span style="color:#ff0000;">verification.error</span>"); return true; } }
处理器
</pre><pre name="code" class="java"><pre name="code" class="java">package com.shiro.controller; import org.apache.shiro.authc.IncorrectCredentialsException; import org.apache.shiro.authc.UnknownAccountException; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; import javax.servlet.http.HttpServletRequest; /** * 常见错误: * subject.login() * DisabledAccountException(禁用的帐号)、 * LockedAccountException(锁定的帐号)、 * UnknownAccountException(错误的帐号)、 * ExcessiveAttemptsException(登录失败次数过多)、 * IncorrectCredentialsException (错误的凭证)、 * ExpiredCredentialsException(过期的凭证) * @author changliang * */ @Controller public class LoginController { @RequestMapping(value = "/login" ) public String showLoginForm(HttpServletRequest req, Model model) { String exceptionClassName = (String)req.getAttribute("shiroLoginFailure"); String error = null; if(UnknownAccountException.class.getName().equals(exceptionClassName)) { error = "用户名/密码错误"; } else if(IncorrectCredentialsException.class.getName().equals(exceptionClassName)) { error = "用户名/密码错误";//错误的凭证 }else if("<span style="color:#ff0000;">verification.error</span>".equals(exceptionClassName)) { error = "验证码错误"; } else if(exceptionClassName != null) { error = "其他错误:" + exceptionClassName; } model.addAttribute("error", error); return "login"; } }
参考 :http://jinnianshilongnian.iteye.com/blog/2049092
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre><pre name="code" class="java">
</pre>
相关文章推荐
- window eclipse 访问远程虚拟机 Hdfs
- java学习笔记5.20
- Myeclipse在线集成svn
- myeclipse10破解后,导出war包时报“SECURITY ALERT: INTEGERITY CHECK ERROR”进行了破解
- 简单谈谈Java类与类之间的关系
- Java的基础语法学习笔记
- Spring 异常捕获+log4j日志
- 292NimGame.java
- 如何手动配置Struts2项目
- Eclipse安装使用git管理项目
- java集合类
- [Java A] – is not an enclosing class
- Java通过ASCII码判断是否为数字
- Struts2框架action路径问题心得----》页面url请求怎么找action
- [译]Spring构建微服务
- JAVA 随机数和数学运算
- eclipse 代码中突然出现特殊字符
- Java内存溢出解决办法
- java字节码入门(上)
- Java设计模式(一) 简单工厂模式不简单