关于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.配置缓存:
<?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>
ehcache.xml
2.在shiro配置文件中,配置缓存的路径:
3.自定义凭证匹配器,引入缓存
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;
}
}
自定义的凭证匹配器
配置该凭证匹配器,并引入缓存:
然后运行登录测试:输入三次错误密码,在登录,报异常账户锁死:
异常类:
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;
}
}
View Code
运行结果: