关于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配置文件中对路径进行配置:

shiro学习笔记:remeberMe,多次登录锁死账号_spring

表示emp/下的请求,用户可以访问,然后运行tomcat,输入表单信息,记住我,然后登录,登录成功后,关闭浏览器,关闭浏览器的同时,关闭了session,但是cookie中存有数据,因此可以直接访问/emp/**下的资源,亲测:火狐浏览器如果出不来效果,可能是设置的问题,火狐浏览器,关闭浏览器时自动清空缓存,需要关闭:

shiro学习笔记:remeberMe,多次登录锁死账号_spring_02

多次登录锁死账号

多次登录锁死账号:根据这个功能的名称,我们可以构思,我们要将登录信息放在哪儿?如果放在cookie中,每次登录就会刷新登录数据,没有办法记录登录的次数,可以缓存来记录登录次数。具体操作:

1.配置缓存:

shiro学习笔记:remeberMe,多次登录锁死账号_缓存_03shiro学习笔记:remeberMe,多次登录锁死账号_spring_04

<?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配置文件中,配置缓存的路径:

shiro学习笔记:remeberMe,多次登录锁死账号_spring_05

3.自定义凭证匹配器,引入缓存

shiro学习笔记:remeberMe,多次登录锁死账号_缓存_03shiro学习笔记:remeberMe,多次登录锁死账号_spring_04

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;
}
}

自定义的凭证匹配器

配置该凭证匹配器,并引入缓存:

shiro学习笔记:remeberMe,多次登录锁死账号_apache_08

然后运行登录测试:输入三次错误密码,在登录,报异常账户锁死:

异常类:

shiro学习笔记:remeberMe,多次登录锁死账号_缓存_03shiro学习笔记:remeberMe,多次登录锁死账号_spring_04

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

运行结果:

shiro学习笔记:remeberMe,多次登录锁死账号_缓存_11