原文 : https://blog.csdn.net/qq_34021712/article/details/80306442
记住我功能在各各网站是比较常见的,实现起来也都差不多,主要就是利用cookie来实现,而shiro对记住我功能的实现也是比较简单的,只需要几步即可。
Shiro提供了记住我(RememberMe)的功能,比如访问一些网站时,关闭了浏览器下次再打开时还是能记住你是谁,下次访问时无需再登录即可访问,基本流程如下:
(1)、首先在登录页面选中RememberMe然后登录成功;如果是浏览器登录,一般会把RememberMe的Cookie写到客户端并保存下来;
(2)、关闭浏览器再重新打开;会发现浏览器还是记住你的;
(3)、访问一般的网页服务器端还是知道你是谁,且能正常访问;
Shiro配置记住我步骤:
在ShiroConfig中添加以下Bean
记住我cookie
/**
* cookie对象;会话Cookie模板 ,默认为: JSESSIONID 问题: 与SERVLET容器名冲突,重新定义为sid或rememberMe,自定义
* @return
*/
@Bean
public SimpleCookie rememberMeCookie(){
//这个参数是cookie的名称,对应前端的checkbox的name = rememberMe
SimpleCookie simpleCookie = new SimpleCookie("rememberMe");
//setcookie的httponly属性如果设为true的话,会增加对xss防护的安全系数。它有以下特点:
//setcookie()的第七个参数
//设为true后,只能通过http访问,javascript无法访问
//防止xss读取cookie
simpleCookie.setHttpOnly(true);
simpleCookie.setPath("/");
//<!-- 记住我cookie生效时间30天 ,单位秒;-->
simpleCookie.setMaxAge(2592000);
return simpleCookie;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
记住我管理器
/**
* cookie管理对象;记住我功能,rememberMe管理器
* @return
*/
@Bean
public CookieRememberMeManager rememberMeManager(){
CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
cookieRememberMeManager.setCookie(rememberMeCookie());
//rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
cookieRememberMeManager.setCipherKey(Base64.decode("4AvVhmFLUs0KTA3Kprsdag=="));
return cookieRememberMeManager;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
记住我Filter
/**
* FormAuthenticationFilter 过滤器 过滤记住我
* @return
*/
@Bean
public FormAuthenticationFilter formAuthenticationFilter(){
FormAuthenticationFilter formAuthenticationFilter = new FormAuthenticationFilter();
//对应前端的checkbox的name = rememberMe
formAuthenticationFilter.setRememberMeParam("rememberMe");
return formAuthenticationFilter;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
在SecurityManager中打开记住我,之前是注释掉的
/**
* 配置核心安全事务管理器
* @param shiroRealm
* @return
*/
@Bean(name="securityManager")
public SecurityManager securityManager(@Qualifier("shiroRealm") ShiroRealm shiroRealm) {
DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
//设置自定义realm.
securityManager.setRealm(shiroRealm);
//配置记住我 参考博客:
securityManager.setRememberMeManager(rememberMeManager());
//配置 redis缓存管理器 参考博客:
//securityManager.setCacheManager(getEhCacheManager());
//配置自定义session管理,使用redis 参考博客:
//securityManager.setSessionManager(sessionManager());
return securityManager;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
修改shirFilter中拦截请求的规则,将/**从authc 改为user
@Bean(name = "shirFilter")
public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
......
// 配置访问权限 必须是LinkedHashMap,因为它必须保证有序
// 过滤链定义,从上向下顺序执行,一般将 /**放在最为下边 --> : 这是一个坑,一不小心代码就不好使了
LinkedHashMap<String, String> filterChainDefinitionMap = new LinkedHashMap<>();
//其他资源都需要认证 authc 表示需要认证才能进行访问 user表示配置记住我或认证通过可以访问的地址
filterChainDefinitionMap.put("/**", "user");
......
return shiroFilterFactoryBean;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
更改LoginController中login方法
接受前台记住我参数,然后传给usernamePasswordToken,将之前的那行注释掉。
@RequestMapping(value = "/login",method = RequestMethod.POST)
public String loginUser(HttpServletRequest request, String username, String password,boolean rememberMe, Model model, HttpSession session) {
//对密码进行加密
//password=new SimpleHash("md5", password, ByteSource.Util.bytes(username.toLowerCase() + "shiro"),2).toHex();
//如果有点击 记住我
UsernamePasswordToken usernamePasswordToken=new UsernamePasswordToken(username,password,rememberMe);
//UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken(username,password);
Subject subject = SecurityUtils.getSubject();
try {
//登录操作
subject.login(usernamePasswordToken);
User user=(User) subject.getPrincipal();
//更新用户登录时间,也可以在ShiroRealm里面做
session.setAttribute("user", user);
model.addAttribute("user",user);
return "index";
} catch(Exception e) {
//登录失败从request中获取shiro处理的异常信息 shiroLoginFailure:就是shiro异常类的全类名
String exception = (String) request.getAttribute("shiroLoginFailure");
model.addAttribute("msg",e.getMessage());
//返回登录页面
return "login";
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
UserInfo实体类实现序列化
UserInfo实体类必须实现序列化接口,否则报序列化异常。因为RememberMe会将用户信息加密然后以Cookie保存。
前台页面添加记住我复选框
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"
xmlns:sec="http://www.thymeleaf.org/thymeleaf-extras-springsecurity3"
xmlns:shiro="http://www.pollix.at/thymeleaf/shiro">
<head>
<meta charset="UTF-8" />
<title>Insert title here</title>
</head>
<body>
<h1>欢迎登录</h1>
<h1 th:if="${msg != null }" th:text="${msg}" style="color: red"></h1>
<form action="/login" method="post">
用户名:<input type="text" name="username"/><br/>
密码:<input type="password" name="password"/><br/>
<input type="checkbox" name="rememberMe" />记住我<br/>
<input type="submit" value="提交"/>
</form>
</body>
</html>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
进行测试
上面的过程已经配置完成,现在进行测试,重启项目,使用admin登录,然后访问:http://localhost:9090/userInfo/view会跳转到登录页面,登录之后退出浏览器,再次点开访问刚才的页面,可以访问。
浏览器cookie查看
重新访问可以访问,我们可以使用浏览器自带的调试功能查看浏览器保存的cookie是什么样的,如下:
rememberMe 大概内容如下:
Ir72ULwc24d/waGzOIYpBUpEakqqezDnuIRr91Lc2UvUVEeln2C+mDICIe338+vZIycabYe0KWuAY7x4PuklO9KeiUu15sHOPJO4bkpYVd4HcoITCi54+KqQhl/47lOO7ucM0BVWkDhEQr3CaFw9lGESg0dZiVrFTDgrwjPBXn0JJzHH7Bs/
itW1IfhqzCJZjUH5nU7J+kG7NQ40f1f2HpZHH0Mlg5zTjCT0JK8Zib2TRd74rYWNnFxtz/
u2C1BrSYGvwdNcI0oouY3CjpJ18PmI6W4MZC4lWzXBPi9OEv8AcHhVRVaCvPrGl52goZr1iOXcM2eOaJoVe5FyBnaD4EWjwFYIZMUPEGXHCsbWsyKp00mQT+/7rQRxd3rq/
v6r+H291CYSq1NfdRDetozMjSVtAMpvceCPbH8tfeHTlbb7B/g1xrsw0BxvsSAWdZm9t6yWP4NREe9bxaPmbmRMklssHLwG63tBgBS2/SOvACeEr0cLJmYIub+3LhVfRwvgjQpW+cRTBA/SnOIPMAw1/8A63BYpgV/9HwckafxKLLn1
0mcSZ7t4ZY8HB+L7ataFZC3aL6s49gbHUhood8yFRreS1yrEqqiucecd+vZFPn4GxHAyayeyI7VXvoaUa8AU6WQ16TBlDXvftOcQQ7maBetfitl7CtZ5pBHDU5YxF6sM5mSH+hIoB+l1o9NLlxnrQg6qAKWGeNao+1lXj/
OjNZu4IZfpJrfkd+NvgHaSyfvVbo8eiedgScATiJ2nzP6NVydWs370LVRazN7LuGcUAz8pdOrlCtwshY/2/jFQNRsUpzuB3Ml1AOrFCVnhyz7bvXqS3+lzQsTsHN370sf35D0dozVEW8wreWG4IfUKydrioUjyeChVga8w5Ir7L46UW
7K1M33gEdpUL1tkda6435EsaCfywRKDy26O4LkcWTLAmDLhvEOGXkxp3guQaZb8VTMTQA7XNU2pzlAdxPJ4JeLAyxExFOS956eGZKbB9qum2+ROc++nb34oAl/9yo0b9eHgh0yZvyWSfi4QVbgrYQ==
- 1
- 2
- 3
- 4
- 5
- 6
- 7
其实是将用户信息加密后放到前台的(包含密码),在项目中user对象信息过于庞大,不能全部存入Cookie,Cookie对长度有一定的限制。