ホームページ  >  記事  >  Java  >  SpringBoot Security がシングル サインアウトを実装し、すべてのトークンをクリアする方法

SpringBoot Security がシングル サインアウトを実装し、すべてのトークンをクリアする方法

WBOY
WBOY転載
2023-05-14 13:43:112049ブラウズ

要件

  • システム A、B、および C は、sso サービスを介してログインを実装します

  • システム A、B、および C は Atoken を取得しますと Btoken をそれぞれ、Ctoken 3 つのトークン

  • システムの 1 つがアクティブにログアウトすると、他の 2 つのシステムもログアウトします

  • これまでのところ、すべてのシステムがログアウトします。 Atoken と Btoken 、 Ctoken が無効です

#レコード トークン

#pom ファイルに依存関係が導入されました

#Redis データベースの依存関係
  • hutool: トークンの解析に使用されます
  • <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>

    トークン ストレージ クラスは AuthJdbcTokenStore を実装します

TokenStore は JdbcTokenStore を継承します
  • ログインしているユーザーのユーザー名を Redis キーとして使用します
  • ユーザーがログインするシステムは複数あるため、値にはトークンを保存する Redis リスト タイプ
  • リスト内のトークンの最大有効時間以上になるように有効時間を設定します
  • @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 JdbcTokenStore を使用してトークンを保存するには、新しいテーブルが必要です
  • 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 は、トークン ストレージに AuthJdbcTokenStore を使用します

JdbcTokenStore の構築メソッドがパスする必要があるため、DataSource を導入します。 DataSource で
  • #TokenStore に従って作成し、AuthJdbcTokenStore を使用して
  • #tokenServices を実装します。TokenStore
  • エンドポイントを追加します。 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

SimpleUrlLogoutSuccessHandler の継承
  • ユーザー名 userName
  • の取得
  • ログイン時に Redis に保存されているトークン リストを取得します
  • ##トークン文字列は OAuth3AccessToken に変換されます

  • TokenStore を使用しますトークンを削除するには

  • @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);
            }
        }
    }

    ログアウト時間が長すぎることを解決してください

  • シナリオ: プロジェクトを一定期間実行した後、ログアウト時間が長すぎることが判明しました。ますます遅くなる

問題: デバッグを通じて、時間がかかるのは主にトークンの削除の期間であることがわかりました

tokenStore.removeAccessToken(token);

理由: 時間が経つにつれて、トークンの数はますます増加します。トークンが大量に存在し、トークン ストレージ テーブル oauth_access_token が異常に大きくなるため、削除効率が非常に悪くなります。

解決策: 他の TokenStore を使用するか、oauth_access_token のテーブル データをクリアします。

以上がSpringBoot Security がシングル サインアウトを実装し、すべてのトークンをクリアする方法の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はyisu.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。