In actual development, the Remember Me function is often enabled for the convenience of user login. If the user selects the “remember me” option when logging in, then within a valid period of time, it will automatically log in by default, eliminating the need to enter the user name, password and other login operations.
The realization mechanism of this function is to generate a Token according to the user’s login information and save it in the cookie of the user’s browser. When the user needs to log in again, it automatically realizes a mechanism to verify and establish a login state.
Remember-Me function in Spring Security
Two implementations of Remember-Me are provided:Spring Security
- Simple encryption Token: Use a hash algorithm to encrypt the user’s necessary login information and generate a Token.
- Persistent Token: Persistent Token used by persistent data storage mechanisms such as databases.
The Remember-Me function needs configure(HttpSecurity http)to be http.rememberMe()configured in the method . This configuration mainly adds the RememberMeAuthenticationFilter filter to the filter chain, and realizes automatic login through this filter. The location of this filter is after other authentication filters, and when other authentication filters are not undergoing authentication processing, this filter will try to work:
Note: The Remember-Me function is used to log in (authentication) again, not to request again.
When the user is successfully logged in and authenticated, there are two cookies in the browser, one is remember-me and the other is JSESSIONID. When the user requests access again, the request is first intercepted by the SecurityContextPersistenceFilter filter, which will obtain the SecurityContext object stored in the corresponding Session according to the JSESSIONID.
If the obtained SecurityContext object stores the authentication user information object Authenticacaion, which means that the thread can directly obtain the authentication user information, the subsequent authentication filter does not need to intercept the request, and remember-me does not work.
When the JSESSIONID expires, only the remember-me cookie exists in the browser. When the user requests access again, because the request does not carry the JSESSIONID, the SecurityContextPersistenceFilter filter cannot obtain the SecurityContext object in the Session, and therefore cannot obtain the authenticated user information, and subsequent login authentication is required. If there is no remember-me cookie, the browser will be redirected to the login page for form login authentication; but the remember-me cookie exists, the RememberMeAuthenticationFilter filter will intercept the request and realize automatic login based on the Token value stored by remember-me.
And store the authenticated user information object Authenticacaion after successful login in the SecurityContext. When the response is returned, the SecurityContextPersistenceFilter filter will store the SecurityContext in the Session, and the next request will obtain the authenticated user information through JSEESIONID.
Demo
In login.html add a Remember Me checkbox, and set ‘remember’ as the name of input
as following code.
login.html
<label class="checkbox">
<input type="checkbox" name="remember"> Remember me
<span class="pull-right"> <a href="#"> Forgot Password?</a></span>
</label>
Then add config in the SecurityConfig
which extends from WebSecurityConfigurerAdapter
. The code sinppet as following.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Resource
private UserService userService;
@Autowired
private DataSource dataSource;
@Autowired
private PersistentTokenRepository persistentTokenRepository;
@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests((authorize) -> authorize
.antMatchers("/css/**", "/fonts/**", "/img/**", "/js/**", "/admin/login").permitAll()
.antMatchers("/user/**").hasAnyRole("USER")
.antMatchers("/**").access("hasRole('ADMIN')")
.anyRequest().authenticated()
)
.formLogin((formLogin) -> formLogin
.loginPage("/admin/login")
...
.permitAll()
)
.logout()
.logoutSuccessHandler(new CustomLogoutSuccessHandler("/admin/logout"))
.permitAll();
http.csrf().disable();
http.rememberMe()
.rememberMeParameter("remember") // keep the parameter same name with login.html
.tokenValiditySeconds(60) // set the toke's valid time to 60s
.userDetailsService(userService)
.tokenRepository(persistentTokenRepository);
}
@Bean
public PersistentTokenRepository persistentTokenRepository() {
JdbcTokenRepositoryImpl jdbcTokenRepository = new JdbcTokenRepositoryImpl();
jdbcTokenRepository.setDataSource(dataSource);
return jdbcTokenRepository;
}
Summary
Remember-me only works when JSESSIONID is invalid and the previous filter authentication fails or is not authenticated. At this time, as long as the cookie of remember-me is not expired, we do not need to fill in the login form to log in again, and after the automatic login of remember-me is successful, a new Token will be generated to replace the old Token, and the corresponding Cookie Max- Age will also be reset.
http.rememberMe()The main methods of return values are explained here. These methods involve Remember-Me configuration, as follows:
rememberMeParameter(String rememberMeParameter)
: Specify the HTTP parameter of “Remember me” when logging in, the default is remember-me.
key(String key)
: The identification field in the Token of “Remember Me”, the default is a random UUID value.
tokenValiditySeconds(int tokenValiditySeconds)
: “Remember me” Token token validity period, the unit is second, that is the Max-Age value of the corresponding cookie, the default time is 2 weeks.
userDetailsService(UserDetailsService userDetailsService)
: Specify the UserDetailsService object used by the Remember-Me function in the automatic login process. By default, the UserDetailsService object in the Spring container is used.
tokenRepository(PersistentTokenRepository tokenRepository)
: Specify the TokenRepository object to configure the persistent Token.
alwaysRemember(boolean alwaysRemember)
: Should I always create a Remember Me Token? The default is false.
useSecureCookie(boolean useSecureCookie)
:Whether the cookie is set to be secure, if it is set to true, the connection request must be made via https.