spring-security authorized access, spring-security automatic login, spring-security persistent token and secondary verification

spring-security authorized access

  1. Add the following configuration in the configure(HttpSecurity) of SecurityConfig, which means that /admin/ needs to have the admin role, /user/ needs to have the user role, and other paths only need to log in. Note that the configuration order cannot be reversed. The matching is from top to bottom. of;
@Override
protected void configure(HttpSecurity http) throws Exception {
    http.authorizeRequests().antMatchers("/admin/**").hasRole("admin")
        .antMatchers("/user/**").hasRole("user").anyRequest().authenticated()
  1. Role inheritance, usually requires the admin role to automatically have the permissions of the user role, the implementation needs to declare the bean of RoleHierarchy, and specify the inheritance relationship;
@Bean
RoleHierarchy roleHierarchy() {
    RoleHierarchyImpl hierarchy = new RoleHierarchyImpl();
    hierarchy.setHierarchy("ROLE_admin > ROLE_user");
    return hierarchy;
}
  1. Finally, according to the implementation of UserDetailService, add the corresponding role to the user in the memory or the database;
@Bean
protected UserDetailsService userDetailsService() {
    InMemoryUserDetailsManager manager = new InMemoryUserDetailsManager();
    manager.createUser(User.withUsername("javaboy").password("123").roles("admin").build());
    manager.createUser(User.withUsername("江南一点雨").password("123").roles("user").build());
    return manager;
}

spring-security automatic login

spring-security provides the remember-me automatic login function, which enables the browser to be reopened after it is closed (session cookie is cleared), and does not need to log in again. The principle is to set the remember-me cookie, and the default validity period is 2 weeks. The next time you request, you will get the remember-me cookie for automatic login verification;

The cookie value of remember-me is obtained by base64 encoding of three parts, base64 (username: timestamp:
md5(username:tokenExpiryTime: password:key)), where key is the salt value of md5, and the default is a uuid. If used The default value, each time the service restarts, the salt value will be regenerated, causing the previously generated cookie to become invalid, so you can manually specify the key;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.rememberMe().key("javaboy");
  1. Remember-me cookie is generated in the onLoginSuccess method of TokenBasedRememberMeServices, set in response, and the default validity period is two weeks;
@Override
public void onLoginSuccess(HttpServletRequest request, HttpServletResponse response,
		Authentication successfulAuthentication) {
	setCookie(new String[] { username, Long.toString(expiryTime), signatureValue },
			tokenLifetime, request, response);
}
  1. Remember-me's cookie verification logic is in the doFilter method of RememberMeAuthenticationFilter. First, there is no login information in the securityContext (the browser is closed and the login cookie is cleared). Call rememberMeServices.autoLogin to automatically log in verification: get the cookie from the request and parse the cookie , According to userName, obtain user information from UserDetailService for verification, verify cookie, and finally generate Authentication and put it into securityContext;
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain)
		throws IOException, ServletException {
	...
	if (SecurityContextHolder.getContext().getAuthentication() == null) {
		Authentication rememberMeAuth = rememberMeServices.autoLogin(request,response);
		...
	}
}
public final Authentication autoLogin(HttpServletRequest request,HttpServletResponse response) {
	String rememberMeCookie = extractRememberMeCookie(request);
	String[] cookieTokens = decodeCookie(rememberMeCookie);
	UserDetails user = processAutoLoginCookie(cookieTokens, request, response);
	userDetailsChecker.check(user);
	return createSuccessfulAuthentication(request, user);
}
  1. The principle of automatic login is that the cookie carries the authentication token, and the cookie has a longer validity period, but this inevitably brings security problems. After the cookie token is leaked, other people can pretend to log in, in order to reduce this risk , There are two methods: persistent token and secondary verification;

Persistent token

The cookie of the persistent token remember-me is composed of series:token. The series is only updated when the user logs in with "Username/Password", and the token is generated for every new request, which makes every time The token will be refreshed when a real user visits, so that the attacker cannot automatically log in using the previous token;

Persistent tokens rely on the database to record token information. By default, this table requires username, series, token and last_used. The primary key is series. The bean of PersistentTokenRepository is declared in the application. This class provides methods for creating, updating, deleting, and obtaining tokens. , And finally need to set the bean to configure(HttpSecurity http) in SecurityConfig;

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.rememberMe().key("javaboy").tokenRepository(jdbcTokenRepository());
}
  1. Persistent token generation. When the user logs in with a password, the token generation is triggered. The series and token of the token are both random strings encoded by base64. Obtain the username from the login information and set the username-series-token-current time. , Save them in pairs in the library table, and finally add cookies to the response;
在类 PersistentTokenBasedRememberMeServices 中:
onLoginSuccess(HttpServletRequest request,HttpServletResponse response, Authentication successfulAuthentication){
	String username = successfulAuthentication.getName();
	PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
		username, generateSeriesData(), generateTokenData(), new Date());
	tokenRepository.createNewToken(persistentToken);
	addCookie(persistentToken, request, response);
}	
  1. Persistent token verification, the next time the user visits, it will carry the remember-me cookie of the persistent token, enter the processAutoLoginCookie method verification, first parse the series and token in the cookie, and use the tokenRepository to check from the library table according to the series Generate the token and compare it. If not, delete the token in the tokenRepository. Automatic login fails (cookies may be stolen). If they are equal, continue to determine the validity period of the token (two weeks by default). After the verification is passed, create a new token. , The series remains unchanged, the time is the latest time, updated to the tokenRepository, and reset to the cookie;
在类 PersistentTokenBasedRememberMeServices 中:
protected UserDetails processAutoLoginCookie(String[] cookieTokens,
	HttpServletRequest request, HttpServletResponse response) {
	final String presentedSeries = cookieTokens[0];
	final String presentedToken = cookieTokens[1];
	PersistentRememberMeToken token = tokenRepository.getTokenForSeries(presentedSeries);
	if (!presentedToken.equals(token.getTokenValue())) {
		tokenRepository.removeUserTokens(token.getUsername());
		throw new CookieTheftException("PersistentTokenBasedRememberMeServices.cookieStolen");
	}
	if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System.currentTimeMillis()) {
		throw new RememberMeAuthenticationException("Remember-me login has expired");
	}
	PersistentRememberMeToken newToken = new PersistentRememberMeToken(
			token.getUsername(), token.getSeries(), generateTokenData(), new Date());
	tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),newToken.getDate());
	addCookie(newToken, request, response);
	return getUserDetailsService().loadUserByUsername(token.getUsername());
}
  1. Persistent token scheme, after the user logs in with a password, a cookie composed of series:token is generated and stored in the database. The next time you visit, the token is found based on the series in the cookie and compared with the token in the cookie If the tokens are equal and within the validity period, the automatic login is successful, a new token is generated, and refreshed in the cookie and database, so that every time a visit is made, the token will be refreshed. Once a third person steals the cookie and makes a visit, the real user will visit next time It will be found that the automatic login is invalid when you log in with a password, and the series and token will be refreshed. This solution can also be used to avoid multi-terminal login;

Secondary check

The solution of persistent tokens cannot perfectly solve the stolen user identity. A secondary verification can be introduced on this basis. For some sensitive operations (update, delete), the password needs to be used again to verify the identity;

The configuration in SecurityConfig is as follows, rememberMe() means that it must be automatically logged in to access, and user/password authentication cannot be accessed.
.fullyAuthenticated() means that automatic login is not included, and only user/password authentication can be accessed. If it is automatic login Password authentication must be performed again. .authenticated() means that user authentication can be accessed, whether it is user/password authentication or automatic login authentication;

@Override
protected void configure(HttpSecurity http) throws Exception {
	http.authorizeRequests().antMatchers("/rememberme").rememberMe()
		.antMatchers("/admin").fullyAuthenticated().anyRequest().authenticated()