Home >Java >javaTutorial >How SpringBoot Security implements single sign-out and clears all tokens
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
pom file introduces dependency
Redis database dependency
hutool: used to parse token
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <dependency> <groupId>cn.hutool</groupId> <artifactId>hutool-all</artifactId> <version>5.7.13</version> </dependency>
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
@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
@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<String> 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 longScenario: After the project has been running for a period of time, it is found that the logout time is getting slower and slowerProblem: 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 poorSolution: 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!