Spring Security-8-记住我 功能实现及原理

一个网站往往是登录一次之后,一周内或者一月内都不需要重新登录, 这里就是用到了记住我的功能,不管是浏览器关闭,或者是我们服务端的服务重启,都没有关系,用户都不需要重新去登录, 下面看一下如何去实现这个功能

原理简介

如下图:
image

先来简单的看一下这个流程

  1. 用户的认证请求过来之后,首先会进入到 UsernamePasswordAuthenticationFilter 这个过滤器
  2. 认证成功之后, 调用 RemeberMeService 根据用户名去生成一个Token
  3. 生成Token之后,由指定的 TokenRepository 去把Token写到DB中去,同时写到浏览器的Cookie中
  4. Cookie和DB中有了token之后,当用户再次登录系统,就会经过 RememberMeAuthenticationFilter 从Cookie中读取Token,然后 RemeberMeService 去DB里面去查询,如果有记录会调用 UserDetailsService 然后根据用户名与密码生成认证的信息

RememberMeAuthenticationFilter 这个过滤器的位置如图:
image

代码实现

前端配置

原理大致了解了之后,看一下我们的项目中应该如何实现

首先,登录页面需要一个复选框 type 是 checkbox, 然后 name 必须是 remember-me,这个是写死的 ,如下:

1
<input type="checkbox" class="checkbox" name="remember-me" >

DB配置

这里的token是放到DB里面的,所以我们还需要在数据库中建一个表,sql如下:

1
2
3
4
5
6
CREATE TABLE persistent_logins (
username VARCHAR (64) NOT NULL,
series VARCHAR (64) PRIMARY KEY,
token VARCHAR (64) NOT NULL,
last_used TIMESTAMP NOT NULL
);

代码中的配置

BrowserSecurityConfig 中需要做出以下配置

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
26
27
@Autowired
private UserDetailsService userDetailsService;

@Autowired
private DataSource dataSource;

@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
return tokenRepository;
}

@Override
protected void configure(HttpSecurity http) throws Exception {

// ... 省略部分代码

http
// ... 省略部分代码
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
// ... 省略部分代码
}

.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds()) 这个配置是配置过期秒数, 然后这里需要在浏览器的配置类中加一个属性,如下:

1
2
3
4
/**
* 记住我的秒数
*/
private int rememberMeSeconds = 60 * 60 * 24;

到这里配置就完成了, 可以运行项目试一下效果

源码分析

RememberMeAuthenticationFilter 这个过滤器是在 UsernamePasswordAuthenticationFilter 后面的, 也就是说只有登录认证成功之后,才会去做记住我的功能

所以这里先看一下认证成功之后,调用的方法,就是 AbstractAuthenticationProcessingFilter 里面的 successfulAuthentication() 方法

image

如上图,首次登录成功之后,会调用 rememberMeServices.loginSuccess(request, response, authResult); 点进去看一下

image

这一步主要就是生产token,分别写到数据库和浏览器的cookie中去

然后看一下下一次登录进来的流程, 首先会进到 RememberMeAuthenticationFilter 中, 然后看一下他的方法

image

这里主要调用了 rememberMeServices.autoLogin(request, response); 这个自动登录的方法,然后看一下这个方法里面

image

这里先是从cookie中组装了一个 UserDetails 对象出来,然后check一次, 最后返回认证对象

总结

用户首次登录成功之后,生成一个token,分别放到数据库和浏览器的cookie中去

第二次登录认证,先判断此次登录是否有经过认证(从session中找),没有的话,调用自动登录, 然后从cookie中拿token, 通过和数据库中的比对, 组合一个 UserDetails, 然后检查返回,最后放到session中去

本文代码传送门