Home>Article>Java> How SpringBoot Security implements single sign-out and clears all tokens

How SpringBoot Security implements single sign-out and clears all tokens

WBOY
WBOY forward
2023-05-14 13:43:11 1983browse

Requirements

  • Systems A, B, and C implement login through the sso service

  • Systems A, B, and C obtain Atoken and Btoken respectively , Ctoken three tokens

  • After one of the systems actively logs out, the other two systems also log out

  • So far all Atoken and Btoken , Ctoken invalid

Record token

pom file introduces dependency

  • Redis database dependency

  • hutool: used to parse token

 org.springframework.boot spring-boot-starter-data-redis   cn.hutool hutool-all 5.7.13 

token storage class implements AuthJdbcTokenStore

  • TokenStore inherits JdbcTokenStore

  • Use the username of the logged-in user username as the Redis key

  • Because there will be multiple systems where the user logs in, the value uses the Redis list type to store the token

  • Set the validity time to ensure that it is no less than the maximum validity time of the token in the list

@Component public class AuthJdbcTokenStore extends JdbcTokenStore { public static final String USER_HAVE_TOKEN = "user-tokens:"; @Resource RedisTemplate redisTemplate; public AuthJdbcTokenStore(DataSource connectionFactory) { super(connectionFactory); } @Override public void storeAccessToken(OAuth3AccessToken token, OAuth3Authentication authentication) { super.storeAccessToken(token, authentication); if (Optional.ofNullable(authentication.getUserAuthentication()).isPresent()) { User user = (User) authentication.getUserAuthentication().getPrincipal(); String userTokensKey = USER_HAVE_TOKEN + user.getUsername(); String tokenValue = token.getValue(); redisTemplate.opsForList().leftPush(userTokensKey, tokenValue); Long seconds = redisTemplate.opsForValue().getOperations().getExpire(userTokensKey); Long tokenExpTime = getExpTime(tokenValue); Long expTime = seconds < tokenExpTime ? tokenExpTime : seconds; redisTemplate.expire(userTokensKey, expTime, TimeUnit.SECONDS); } } private long getExpTime(String accessToken) { JWT jwt = JWTUtil.parseToken(accessToken); cn.hutool.json.JSONObject jsonObject = jwt.getPayload().getClaimsJson(); long nowTime = Instant.now().getEpochSecond(); long expEndTime = jsonObject.getLong("exp"); long expTime = (expEndTime - nowTime); return expTime; } }

oauth_access_token Using JdbcTokenStore to store tokens requires a new table

CREATE TABLE `oauth_access_token` ( `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP, `token_id` varchar(255) DEFAULT NULL, `token` blob, `authentication_id` varchar(255) DEFAULT NULL, `user_name` varchar(255) DEFAULT NULL, `client_id` varchar(255) DEFAULT NULL, `authentication` blob, `refresh_token` varchar(255) DEFAULT NULL, UNIQUE KEY `authentication_id` (`authentication_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

AuthorizationServerConfigurerAdapter uses AuthJdbcTokenStore for token storage

  • Introducing DataSource, because the construction method of JdbcTokenStore must pass in DataSource

  • Create according to TokenStore, Use AuthJdbcTokenStore to implement

  • ##tokenServices Add TokenStore

  • endpoints Add tokenServices

  • @Configuration @EnableAuthorizationServer public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter { @Autowired private DataSource dataSource; ... @Bean public TokenStore tokenStore() { JdbcTokenStore tokenStore = new AuthJdbcTokenStore(dataSource); return tokenStore; } @Override public void configure(AuthorizationServerEndpointsConfigurer endpoints) { DefaultTokenServices tokenServices = new DefaultTokenServices(); tokenServices.setTokenStore(tokenStore()); endpoints .authenticationManager(authenticationManager) .tokenServices(tokenServices) .accessTokenConverter(converter) ; } ... }
Clear token

  • Inherit SimpleUrlLogoutSuccessHandler

  • Get the username userName

  • Get the token list stored in Redis during login

  • Token string is converted into OAuth3AccessToken

  • Use tokenStore to delete token

  • @Component public class AuthLogoutSuccessHandler1 extends SimpleUrlLogoutSuccessHandler { String USER_HAVE_TOKEN = AuthJdbcTokenStore.USER_HAVE_TOKEN; @Resource RedisTemplate redisTemplate; @Resource TokenStore tokenStore; @Override public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { if (!Objects.isNull(authentication)) { String userName = authentication.getName(); String userTokensKey = USER_HAVE_TOKEN + userName; Long size = redisTemplate.opsForList().size(userTokensKey); List list = redisTemplate.opsForList().range(userTokensKey, 0, size); for (String tokenValue : list) { OAuth3AccessToken token = tokenStore.readAccessToken(tokenValue); if (Objects.nonNull(token)) { tokenStore.removeAccessToken(token); } } redisTemplate.delete(userTokensKey); super.handle(request, response, authentication); } } }
Solve the logout time Too long

Scenario: After the project has been running for a period of time, it is found that the logout time is getting slower and slower

Problem: Through debugging, it is found that the time consuming is mainly in the period of deleting the token

tokenStore.removeAccessToken(token);

Reason: As time goes by, there are more and more tokens, and the token storage table oauth_access_token becomes abnormally large, so the deletion efficiency is very poor

Solution: Use other TokenStore, or clear the table data of oauth_access_token

The above is the detailed content of How SpringBoot Security implements single sign-out and clears all tokens. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:yisu.com. If there is any infringement, please contact admin@php.cn delete