Spring Security 实现“记住我”功能,即自动登录功能有两种方式:
- 将 token写入到浏览器的 Cookie中
- 将 token持久化到数据库
1.1 后端 Spring Security默认是没有开启“记住我”功能,我们在 Spring Security配置类中开启它即可。
// 开启记住我功能
.rememberMe()
.key("rememberMeKey") // 默认 key为UUID,我们自定义 key
.tokenValiditySeconds(60) //设置token的过期时间,默认2周
注意:
key 默认值是一个 UUID 字符串,如果服务端重启,这个 key 会变,这样会导致之前所有 remember-me 自动登录令牌失效,所以,我们一般都指定 key值。
1.2 前端 前端需要注意:
- 记住我的字段名称 默认是
remember-me
- remember-me的值必须是
true | on | yes | "1"
这些字段名可以自定义,我们使用默认值就行。
1.3 测试 测试一下,登录认证通过后,关掉浏览器,再次打开页面,remember-me功能生效了,就这么简单。 我们将 remember-me的值,通过 Base64 转码后的字符串,得到:
可以看到,cookie 中 remember-me 的使用用 : 隔开,分成了三部分:
- 用户名。
- 时间戳,即 token的过期时间。
- 是使用 MD5 散列函数算出来的值,它的明文格式是 username + “:” + tokenExpiryTime + “:” + password + “:” + key,最后的 key 是一个散列盐值,可以用来防治令牌被修改。
前面分析登录认证流程时,认证成功就会调用“记住我”功能。
- Spring Security登录认证源码分析:https://blog.csdn.net/qq_42402854/article/details/122295175
查看 UsernamePasswordAuthenticationFilter的父类 AbstractAuthenticationProcessingFilter过滤器的 doFIlter方法中,认证做了两个分支,
- 成功执行 successfulAuthentication,
- 失败执行 unsuccessfulAuthentication。
在 successfulAuthentication内部,将用户认证信息存储到了 SecurityContext中,并调用了 loginSuccess方法,这就是“记住我”功能的核心方法。
查看 AbstractRememberMeServices类的 loginSuccess方法
。
private String parameter = "remember-me";
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response,
Authentication successfulAuthentication) {
// 判断是否勾选记住我
// 注意:这里this.parameter点进去是上面的private String parameter = "remember-me";
if (!this.rememberMeRequested(request, this.parameter)) {
this.logger.debug("Remember-me login not requested.");
} else {
//若勾选就调用onLoginSuccess方法
this.onLoginSuccess(request, response, successfulAuthentication);
}
}
1)“记住我”表单属性的名称和值
protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
if (this.alwaysRemember) {
return true;
} else {
// "remember-me"属性名默认为"remember-me"
String paramValue = request.getParameter(parameter);
// 这属性值可以为:true,on,yes,1。
if (paramValue != null && (paramValue.equalsIgnoreCase("true") ||
paramValue.equalsIgnoreCase("on") || paramValue.equalsIgnoreCase("yes") ||
paramValue.equals("1"))) {
//满足上面条件才能返回true
return true;
} else {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Did not send remember-me cookie (principal did not set
parameter '" + parameter + "')");
}
return false;
}
}
}
2)查看 TokenBasedRememberMeServices类的 onLoginSuccess方法。
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
//1.获取用户信息
String username = this.retrieveUserName(successfulAuthentication);
String password = this.retrievePassword(successfulAuthentication);
if (!StringUtils.hasLength(username)) {
this.logger.debug("Unable to retrieve username");
} else {
if (!StringUtils.hasLength(password)) {
UserDetails user = this.getUserDetailsService().loadUserByUsername(username);
password = user.getPassword();
if (!StringUtils.hasLength(password)) {
this.logger.debug("Unable to obtain password for user: " + username);
return;
}
}
//2.获取token的有效期
int tokenLifetime = this.calculateLoginLifetime(request, successfulAuthentication);
long expiryTime = System.currentTimeMillis();
expiryTime += 1000L * (long)(tokenLifetime
关注
打赏
最近更新
- 深拷贝和浅拷贝的区别(重点)
- 【Vue】走进Vue框架世界
- 【云服务器】项目部署—搭建网站—vue电商后台管理系统
- 【React介绍】 一文带你深入React
- 【React】React组件实例的三大属性之state,props,refs(你学废了吗)
- 【脚手架VueCLI】从零开始,创建一个VUE项目
- 【React】深入理解React组件生命周期----图文详解(含代码)
- 【React】DOM的Diffing算法是什么?以及DOM中key的作用----经典面试题
- 【React】1_使用React脚手架创建项目步骤--------详解(含项目结构说明)
- 【React】2_如何使用react脚手架写一个简单的页面?