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

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

WBOY
Release: 2023-05-14 13:43:11
forward
2090 people have browsed it

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

<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>
Copy after login

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;
    }
}
Copy after login

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;
Copy after login

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)
            ;
        }
    	...
    }
    Copy after login
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<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);
            }
        }
    }
    Copy after login
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);
Copy after login
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!

Related labels:
source:yisu.com
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template