shiro学习笔记:remeberMe,多次登录锁死账号
关于shiro的rememberme的实现,再之前我们是使用cookie实现的,这里也是一样,原理都是相同的;
不过因为用到了shiro框架,因此需要再shiro中配置cookie以及缓存等,以及管理器对象:
<!--安全管理器--> <bean id="securityManager" class="org.apache.shiro.web.mgt.DefaultWebSecurityManager"> <!--缓存管理器--> <property name="cacheManager" ref="cacheManager"/> <!--会话的模式--> <property name="sessionMode" value="native"/> <!--配置realm--> <property name="realms" ref="myRealm"/> <property name="rememberMeManager" ref="rememberMeManager"/> </bean> <!--cookie对象--> <bean id="simpleCookie" class="org.apache.shiro.web.servlet.SimpleCookie"> <constructor-arg value="rememberMe"/> <!--只有http的连接才使用cookie--> <property name="httpOnly" value="true"/> <!--cookie失效时间--> <property name="maxAge" value="#{60*60*24}"/> </bean> <!--记住我管理器对象--> <bean id="rememberMeManager" class="org.apache.shiro.web.mgt.CookieRememberMeManager"> <property name="cookie" ref="simpleCookie"/> </bean>
控制器:
@Controller public class UserController { private static final transient Logger log = LoggerFactory.getLogger(UserController.class); @RequestMapping("/login") public String login(User user,String rememberMe) throws Exception { Subject currentUser = SecurityUtils.getSubject(); UsernamePasswordToken token = new UsernamePasswordToken(user.getUsername(), user.getPassword()); //记住我 if (rememberMe != null &&rememberMe.equals("1")) { token.setRememberMe(true); } currentUser.login(token); return "redirect:/home"; } }
前端页面:
<form action="../login" method="post"> <input type="text" name="username"> <input type="password" name="password"> <input type="checkbox" name="rememberMe" value="1">记住我 <input type="submit"> </form>
配置完成后,再shiro配置文件中对路径进行配置:
表示emp/下的请求,用户可以访问,然后运行tomcat,输入表单信息,记住我,然后登录,登录成功后,关闭浏览器,关闭浏览器的同时,关闭了session,但是cookie中存有数据,因此可以直接访问/emp/**下的资源,亲测:火狐浏览器如果出不来效果,可能是设置的问题,火狐浏览器,关闭浏览器时自动清空缓存,需要关闭:
多次登录锁死账号
多次登录锁死账号:根据这个功能的名称,我们可以构思,我们要将登录信息放在哪儿?如果放在cookie中,每次登录就会刷新登录数据,没有办法记录登录的次数,可以缓存来记录登录次数。具体操作:
1.配置缓存:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
<?xml version="1.0" encoding="UTF-8"?> <ehcache name="es"> <diskStore path="java.io.tmpdir"/> <!--maxEntriesLocalHeap:是用来限制当前缓存在堆内存上所能保存的最大元素数量 eternal:false 设定缓存的elemen是否永远不过期 timeToLiveSeconds:对象存活时间,指对象从创建到失效所需要的时间。只对eternal为false的有效。默认值为0,表示一直可以访问。(单位:秒) timeToIdleSeconds:对象空闲时,指对象在多长时间没有被访问就会失效。只对eternal为false的有效。默认值为0。(单位:秒) --> <!-- 默认缓存 --> <defaultCache maxEntriesLocalHeap="10000" eternal="false" timeToIdleSeconds="120" timeToLiveSeconds="120" maxEntriesLocalDisk="10000000" diskExpiryThreadIntervalSeconds="120" memoryStoreEvictionPolicy="LRU"/> <!-- 登录记录缓存 锁定10分钟 --> <cache name="passwordRetryCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="360" timeToLiveSeconds="360" overflowToDisk="false" statistics="true"> </cache> <cache name="authorizationCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> <cache name="authenticationCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> <cache name="shiro-activeSessionCache" maxEntriesLocalHeap="2000" eternal="false" timeToIdleSeconds="3600" timeToLiveSeconds="0" overflowToDisk="false" statistics="true"> </cache> </ehcache>
2.在shiro配置文件中,配置缓存的路径:
3.自定义凭证匹配器,引入缓存
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zs.credentials; import org.apache.shiro.authc.AuthenticationInfo; import org.apache.shiro.authc.AuthenticationToken; import org.apache.shiro.authc.ExcessiveAttemptsException; import org.apache.shiro.authc.credential.HashedCredentialsMatcher; import org.apache.shiro.cache.Cache; import org.apache.shiro.cache.CacheManager; import java.util.concurrent.atomic.AtomicInteger; public class MyMatcher extends HashedCredentialsMatcher { //Map:key,value //key:存用户名 value:次数 private Cache<String, AtomicInteger> passwordCache; public MyMatcher(CacheManager cacheManager) { this.passwordCache = cacheManager.getCache("passwordRetryCache"); } //密码匹配 @Override public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //获取用户名 String username= (String) token.getPrincipal(); //先去缓存中查找是否有该信息 AtomicInteger retryCount= passwordCache.get(username); //第一次是null if(retryCount==null){ //初始话:0 retryCount=new AtomicInteger(0); //存入缓存中 passwordCache.put(username,retryCount); } //在retryCount上增加1,并获取该值 if(retryCount.incrementAndGet()>3){ throw new ExcessiveAttemptsException("该账号已锁定"); } //密码匹配 boolean matcher=super.doCredentialsMatch(token, info); //如果登录成功 if(matcher){ //清空缓存数据 passwordCache.remove(username); } return matcher; } }
配置该凭证匹配器,并引入缓存:
然后运行登录测试:输入三次错误密码,在登录,报异常账户锁死:
异常类:
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
package com.zs.controller; import com.zs.entity.Result; import org.apache.shiro.authc.AccountException; import org.apache.shiro.authc.ExcessiveAttemptsException; import org.apache.shiro.authc.IncorrectCredentialsException; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ResponseBody; @ControllerAdvice public class ExceptionController { @ExceptionHandler(Exception.class) @ResponseBody public Result error(Exception ex, Model model) { Result result = new Result(); if (ex instanceof AccountException) { if (ex instanceof ExcessiveAttemptsException) { result.setMessages("账户锁定"); } else { result.setStats(501); result.setMessages("账号错误"); } } else if (ex instanceof IncorrectCredentialsException) { result.setStats(502); result.setMessages("密码错误"); } else { result.setStats(503); result.setMessages("数据错误"); } return result; } }
运行结果: