在用户登陆一次以后,系统会记住用户一段时间,在这段时间,用户不用反复登陆就可以使用我们的系统。

- 用户登录时,请求先进入
UsernamePasswordAuthenticationFilter,当这个过滤器认证成功之后,会调用一个RemeberMeService服务,在RemeberMeService类里面有一个TokenRepository方法。RemeberMeService会生成一个token,然后将这个token存入到浏览器的Cookie中去。同时TokenRepository方法将这个Token写入到数据库中,因为这个动作是在通过UsernamePasswordAuthenticationFilter认证成功之后去做的,所以在存入数据的时候会将用户名和token存入进去,即token和用户名是一一对应的。 - 当同一个用户再次访问系统的时候,不需要登录了,这个请求在经过过滤器链的时候会经过
RememberMeAuthenticationFilter,这个过滤器的作用就是读取cookie中的token,然后交给RemeberMeService,RemeberMeService会用TokenRepository到数据库中去查询这个token在数据库中有没有记录,如果有记录会将用户名取出来,取出来之后会调用UserDetailsService去获取用户信息,然后将用户信息存入到SecurityContext中去,这样用户就登录了。

RememberMeAuthenticationFilter位于过滤器链中的倒数第二个位置,当其他用户认证都无法认证用户信息时,RememberMe会尝试做认证。
代码实现
在用户认证配置中,注入DataSource与UserDetailsService,在configure方法中添加rememberMe()配置。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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69// BrowserSecurityConfig.java
(SecurityProperties.class)
@ComponentScan(basePackages = {"net.ling.security"})
public class BrowserSecurityConfig extends WebSecurityConfigurerAdapter {
private SecurityProperties securityProperties;
private DataSource dataSource;
private UserDetailsService userDetailsService;
private AuthenticationSuccessHandler imoocAuthenticationSuccessHandler;
private AuthenticationFailureHandler imoocAuthenticationFailureHandler;
public PasswordEncoder passwordEncoder() {
return new BCryptPasswordEncoder();
}
public PersistentTokenRepository persistentTokenRepository(){
JdbcTokenRepositoryImpl tokenRepository=new JdbcTokenRepositoryImpl();
tokenRepository.setDataSource(dataSource);
// 启动的时候自动创建表
// tokenRepository.setCreateTableOnStartup(true);
return tokenRepository;
}
protected void configure(HttpSecurity http) throws Exception {
ValidateCodeFilter validateCodeFilter=new ValidateCodeFilter();
validateCodeFilter.setAuthenticationFailureHandler(imoocAuthenticationFailureHandler);
validateCodeFilter.setSecurityProperties(securityProperties);
validateCodeFilter.afterPropertiesSet();
http.addFilterBefore(validateCodeFilter, UsernamePasswordAuthenticationFilter.class)
.formLogin()
.loginPage("/authentication/require")
.loginProcessingUrl("/authentication/form")
.successHandler(imoocAuthenticationSuccessHandler)
.failureHandler(imoocAuthenticationFailureHandler)
.and()
.authorizeRequests()
.antMatchers("/authentication/require", "/swagger-ui.html","/webjars/**","/v2/**","/swagger-resources/**","/imooc-signIn.html",securityProperties.getBrowser().getLoginPage(),"/code/image").permitAll()
.anyRequest()
.authenticated()
.and()
.logout()
.and()
// rememberMe start
.rememberMe()
.tokenRepository(persistentTokenRepository())
.tokenValiditySeconds(securityProperties.getBrowser().getRememberMeSeconds())
.userDetailsService(userDetailsService)
// rememberMe end
.and()
.csrf().disable();
}
}
在配置中,新增rememberMeSeconds的超时时间配置。1
2
3
4
5
6
7
8
9
10// BrowserProperties.java
public class BrowserProperties {
private String loginPage="/imooc-signIn.html";
private LoginResponseType loginType= LoginResponseType.JSON;
private int rememberMeSeconds=3600; // 一小时
}
基本原理
第一次登录

请求会进入UsernamePasswordAuthenticationFilter中,校验完用户名密码之后会调用rememberMeServices.loginSuccess(request, response, authResult);方法。

- 用tokenRepository去创建一个新的token存入到数据库中。
- 将生成的token存入到浏览器的cookie中去。
在有RememberMe的情况下再次登录


- 从请求的cookies中拿到token和tokenSeries。
- 从数据库中通过token和tokenSeries获取用户信息。
- 如果查不到相关的用户信息,就会抛出异常;如果有数据,则进行和一系列的判断,如:是否已经过期
- 如果检查都已经通过,通过找到的用户信息中的用户名,调用
UerDetailsService获取详细的用户信息。
存储RememberMe获取到的用户信息

将获取的用户信息存储至SecurityContextHolder中。