Here’s a scenario — a user visits your site for the first time and logs in, then comes back the next day and has to log in again. So there are features like “Remember me” to make it easier for users to use, but one thing is self-evident, that is, the “prolonged” authentication status has long been beyond the scope of users’ original needs. This means that they can close the browser, and then shut down the computer, the next week or next month, and even more will come back later, as long as the interval time not too far, the site will know who is who, and, as always, to provide all of the same functions and services, and for a long time before they leave.
Remember my fundamentals
- Called after the user is successfully authenticated
RemeberMeService
Generated based on the user nameToken
byTokenRepository
Write to the database, also willToken
Write to the browserCookie
In the - After the service is restarted, the user will log in to the system again
RememberMeAuthenticationFilter
Interception, fromCookie
Reads theToken
Information, andpersistent_logins
The table matches to determine whether the remember me function is used. The most ofUserDetailsService
Querying User Information
Remember me to achieve
- create
persistent_logins
table
create table persistent_logins (username varchar(64) not null, series varchar(64) primary key, token varchar(64) not null, last_used timestamp not null);
Copy the code
- Add the Remember me check item to the landing page (Name must be remeber-me)
<input name="remember-me" type="checkbox">Next Automatic LoginCopy the code
- Configuration MerryyouSecurityConfig
http.
......
.and()
.rememberMe()
.tokenRepository(persistentTokenRepository())// Set the Repository for the action table
.tokenValiditySeconds(securityProperties.getRememberMeSeconds())// Set remember my time
.userDetailsService(userDetailsService)/ / set the userDetailsService
.and()
......
Copy the code
Results the following
Source code analysis
For the first time login
AbstractAuthenticationProcessingFilter#successfulAuthentication
protected void successfulAuthentication(HttpServletRequest request, HttpServletResponse response, FilterChain chain, Authentication authResult)
throws IOException, ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Authentication success. Updating SecurityContextHolder to contain: "
+ authResult);
}
//# 1. Put the authenticated Authentication into the SecurityContext
SecurityContextHolder.getContext().setAuthentication(authResult);
//# 2. Login successfully to call rememberMeServices
rememberMeServices.loginSuccess(request, response, authResult);
// Fire event
if (this.eventPublisher ! =null) {
eventPublisher.publishEvent(new InteractiveAuthenticationSuccessEvent(
authResult, this.getClass()));
}
successHandler.onAuthenticationSuccess(request, response, authResult);
}
Copy the code
- Put the authenticated Authentication into the SecurityContext
- Login successfully invoked rememberMeServices
AbstractRememberMeServices#loginSuccess
private String parameter = DEFAULT_PARAMETER;//remember-me
public final void loginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
// #1. Check whether remember me is checked
if(! rememberMeRequested(request, parameter)) { logger.debug("Remember-me login not requested.");
return;
}
onLoginSuccess(request, response, successfulAuthentication);
}
Copy the code
- Check whether remember me is checked
PersistentTokenBasedRememberMeServices#onLoginSuccess
protected void onLoginSuccess(HttpServletRequest request, HttpServletResponse response, Authentication successfulAuthentication) {
//#1. Get the username
String username = successfulAuthentication.getName();
logger.debug("Creating new persistent login for user " + username);
/ / # 2. Create a Token
PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
username, generateSeriesData(), generateTokenData(), new Date());
try {
//#3. Store the database
tokenRepository.createNewToken(persistentToken);
//#4. Write to the browser Cookie
addCookie(persistentToken, request, response);
}
catch (Exception e) {
logger.error("Failed to save persistent token ", e); }}Copy the code
- Obtaining a user name
- Create a Token
- Storage capital database
- Write to a browser Cookie
Second login Remember-me
RememberMeAuthenticationFilter#doFilter
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
//#1. Check that there is no Authentication in SecurityContext
if (SecurityContextHolder.getContext().getAuthentication() == null) {
/ / # 2. From cookies return to RememberMeAuthenticationToken query user information
Authentication rememberMeAuth = rememberMeServices.autoLogin(request,
response);
if(rememberMeAuth ! =null) {
// Attempt authenticaton via AuthenticationManager
try {
//#3. If not empty, authenticationManager authenticates
rememberMeAuth = authenticationManager.authenticate(rememberMeAuth);
// Store to SecurityContextHolderSecurityContextHolder.getContext().setAuthentication(rememberMeAuth); onSuccessfulAuthentication(request, response, rememberMeAuth); .Copy the code
- Check that there is no Authentication in the SecurityContext
- From cookies return to RememberMeAuthenticationToken query user information
- If not empty, authenticationManager authenticates it
AbstractRememberMeServices#autoLogin
public final Authentication autoLogin(HttpServletRequest request, HttpServletResponse response) {
/ / # 1. Get a Cookie
String rememberMeCookie = extractRememberMeCookie(request);
if (rememberMeCookie == null) {
return null;
}
logger.debug("Remember-me cookie detected");
if (rememberMeCookie.length() == 0) {
logger.debug("Cookie was empty");
cancelCookie(request, response);
return null;
}
UserDetails user = null;
try {
/ / # 2. Analytical cookies
String[] cookieTokens = decodeCookie(rememberMeCookie);
//#3
user = processAutoLoginCookie(cookieTokens, request, response);
//#4. Check user credentials
userDetailsChecker.check(user);
logger.debug("Remember-me cookie accepted");
/ / # 5. Return the Authentication
return createSuccessfulAuthentication(request, user);
}
catch (CookieTheftException cte) {
cancelCookie(request, response);
throw cte;
}
catch (UsernameNotFoundException noUser) {
logger.debug("Remember-me login was valid but corresponding user not found.",
noUser);
}
catch (InvalidCookieException invalidCookie) {
logger.debug("Invalid remember-me cookie: " + invalidCookie.getMessage());
}
catch (AccountStatusException statusInvalid) {
logger.debug("Invalid UserDetails: " + statusInvalid.getMessage());
}
catch (RememberMeAuthenticationException e) {
logger.debug(e.getMessage());
}
cancelCookie(request, response);
return null;
}
Copy the code
- To get a Cookie
- Parsing the Cookie
- Obtaining user credentials
- Checking user credentials
The code download
Download it from my Github, github.com/longfeizhen…