Spring Security study notes (nine) RememberMe advanced

Reference video, bad programming people

Earlier we introduced the implementation principle of rememberMe, from which we can think about such a problem, if our cookie is obtained by an illegal user, and then carry this cookie to access the content of our project, it will lead to illegal user login. How to solve this problem?

RememberMeAdvanced

We mentioned before that the generation and verification of cookies are all TokenBasedRememberMeServicesdone in this class. RememberMeServicesThis interface also has an implementation class PersistentTokenBasedRememberMeServices
insert image description here
PersistentTokenBasedRememberMeServicesthat is an advanced version of rememberMe. The generated cookie is Seriescomposed TokenValueof

protected void onLoginSuccess(HttpServletRequest request,
			HttpServletResponse response, Authentication successfulAuthentication) {
    
    
		String username = successfulAuthentication.getName();

		logger.debug("Creating new persistent login for user " + username);

		PersistentRememberMeToken persistentToken = new PersistentRememberMeToken(
				username, generateSeriesData(), generateTokenData(), new Date());
		try {
    
    
			tokenRepository.createNewToken(persistentToken);
			//这个生成的cookie
			addCookie(persistentToken, request, response);
		}
		catch (Exception e) {
    
    
			logger.error("Failed to save persistent token ", e);
		}
	}
private void addCookie(PersistentRememberMeToken token, HttpServletRequest request,
			HttpServletResponse response) {
    
    
		setCookie(new String[] {
    
     token.getSeries(), token.getTokenValue() },
				getTokenValiditySeconds(), request, response);
	}

If the next session expires, the following authentication logic will be followed, and PersistentTokenRepository the implementation here is based on memory by default.

private PersistentTokenRepository tokenRepository = new InMemoryTokenRepositoryImpl();
protected UserDetails processAutoLoginCookie(String[] cookieTokens,
			HttpServletRequest request, HttpServletResponse response) {
    
    

		if (cookieTokens.length != 2) {
    
    
			throw new InvalidCookieException("Cookie token did not contain " + 2
					+ " tokens, but contained '" + Arrays.asList(cookieTokens) + "'");
		}

		final String presentedSeries = cookieTokens[0];
		final String presentedToken = cookieTokens[1];

		PersistentRememberMeToken token = tokenRepository
				.getTokenForSeries(presentedSeries);

		if (token == null) {
    
    
			// No series match, so we can't authenticate using this cookie
			throw new RememberMeAuthenticationException(
					"No persistent token found for series id: " + presentedSeries);
		}

		// We have a match for this user/series combination
		//这里对比内存中token对应的value和cookie里面的进行对比,如果相同则认证通过
		if (!presentedToken.equals(token.getTokenValue())) {
    
    
			// Token doesn't match series value. Delete all logins for this user and throw
			// an exception to warn them.
			tokenRepository.removeUserTokens(token.getUsername());

			throw new CookieTheftException(
					messages.getMessage(
							"PersistentTokenBasedRememberMeServices.cookieStolen",
							"Invalid remember-me token (Series/token) mismatch. Implies previous cookie theft attack."));
		}

		if (token.getDate().getTime() + getTokenValiditySeconds() * 1000L < System
				.currentTimeMillis()) {
    
    
			throw new RememberMeAuthenticationException("Remember-me login has expired");
		}

		// Token also matches, so login is valid. Update the token value, keeping the
		// *same* series number.
		if (logger.isDebugEnabled()) {
    
    
			logger.debug("Refreshing persistent login token for user '"
					+ token.getUsername() + "', series '" + token.getSeries() + "'");
		}

//下面这一块的逻辑就是把cookie进行一个更新,也就是说一旦会话失效,如果使用了之前的cookie就会生成新
//的cookie,原阿里的cookie九无法使用了。这在一定程度上增加了安全性
		PersistentRememberMeToken newToken = new PersistentRememberMeToken(
				token.getUsername(), token.getSeries(), generateTokenData(), new Date());

		try {
    
    
		//这里更新的时候series是不变的,变的是series对应的value值
			tokenRepository.updateToken(newToken.getSeries(), newToken.getTokenValue(),
					newToken.getDate());
			addCookie(newToken, request, response);
		}
		catch (Exception e) {
    
    
			logger.error("Failed to update token: ", e);
			throw new RememberMeAuthenticationException(
					"Autologin failed due to data access problem");
		}

		return getUserDetailsService().loadUserByUsername(token.getUsername());
	}

RememberMe's persistent token

For the previous memory-based remember-me function, once the project is restarted, you need to log in again, which sometimes does not meet the requirements. We need to put this memory-based implementation into the database for implementation.

insert image description here
The default is memory-based, and we can customize the database-based implementation.

In the configuration class add

@Bean
    protected PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices(){
    
    
        PersistentTokenBasedRememberMeServices persistentTokenBasedRememberMeServices =
//                new PersistentTokenBasedRememberMeServices(UUID.randomUUID().toString(), myUserDetailService, new InMemoryTokenRepositoryImpl());
                new PersistentTokenBasedRememberMeServices(UUID.randomUUID().toString(), myUserDetailService, jdbcTokenRepository());
        return persistentTokenBasedRememberMeServices;
    }

    @Bean
    protected JdbcTokenRepositoryImpl jdbcTokenRepository(){
    
    
        JdbcTokenRepositoryImpl tokenRepository = new JdbcTokenRepositoryImpl();
        tokenRepository.setDataSource(dataSource);
        tokenRepository.setCreateTableOnStartup(true);
        return tokenRepository;
    }

insert image description here
At this time, when the project is started, the table structure will be generated.
insert image description here
At this time, when I log in to the user, the corresponding cookie will be generated and persisted to the database.
insert image description here
At this time, if I restart the project and refresh the page, there is no need to log in again. Comment out this sentence when restarting

//        tokenRepository.setCreateTableOnStartup(true);

After refreshing,
insert image description here
it can be found that the value of token has changed, but the value of series has not changed, which is consistent with our above analysis.

For front-end and back-end separation projects, if you want to set rememberMe, you need to modify the following part
insert image description here

protected boolean rememberMeRequested(HttpServletRequest request, String parameter) {
    
    
		if (alwaysRemember) {
    
    
			return true;
		}

		String paramValue = request.getParameter(parameter);

		if (paramValue != null) {
    
    
			if (paramValue.equalsIgnoreCase("true") || paramValue.equalsIgnoreCase("on")
					|| paramValue.equalsIgnoreCase("yes") || paramValue.equals("1")) {
    
    
				return true;
			}
		}

		if (logger.isDebugEnabled()) {
    
    
			logger.debug("Did not send remember-me cookie (principal did not set parameter '"
					+ parameter + "')");
		}

		return false;
	}

We need to write a subclass to override the rememberMeRequested method of the parent class. For specific methods, please refer to the video of bad people

Guess you like

Origin blog.csdn.net/qq_45401910/article/details/127195848