Rumah  >  Artikel  >  Java  >  Cara SpringBoot Security melaksanakan log keluar tunggal dan mengosongkan semua token

Cara SpringBoot Security melaksanakan log keluar tunggal dan mengosongkan semua token

WBOY
WBOYke hadapan
2023-05-14 13:43:111996semak imbas

Keperluan

  • Sistem A, B dan C boleh log masuk melalui perkhidmatan sso

  • Sistem A, B dan C memperoleh Atoken dan Btoken masing-masing , Ctoken tiga token

  • Selepas salah satu sistem log keluar secara aktif, dua sistem lain turut log keluar

  • Setakat ini semua Atoken dan Btoken , Ctoken tidak sah

Token rekod

fail pom memperkenalkan kebergantungan

  • Kebergantungan pangkalan data Redis

  • hutool: digunakan untuk menghuraikan 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>

kelas storan token melaksanakan AuthJdbcTokenStore

  • TokenStore>mewarisi JdbcToken>

  • Gunakan nama pengguna pengguna log masuk sebagai kunci Redis

  • Oleh kerana akan terdapat berbilang sistem di mana pengguna log masuk, nilai menggunakan Jenis senarai redis untuk menyimpan token

  • Tetapkan masa sah untuk memastikan bahawa ia tidak kurang daripada masa sah maksimum token dalam senarai

  • @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 Menggunakan JdbcTokenStore untuk menyimpan token memerlukan jadual baharu

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 menggunakan AuthJdbcTokenStore untuk storan token

  • Melalui kaedah Pembinaan Jbc. dalam DataSource

  • Buat TokenStore Gunakan AuthJdbcTokenStore untuk melaksanakan

  • tokenServices Tambah TokenStore

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

  • Warisi SimpleUrlLogoutSuccessHandler

  • Dapatkan nama pengguna

  • Dapatkan senarai token yang disimpan dalam Redis semasa log masuk
  • Rentetan token ditukar kepada OAuth3AccessToken
  • Gunakan tokenStore untuk memadam 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);
            }
        }
    }
  • Untuk menyelesaikan masa log keluar Terlalu lama

Senario: Selepas projek telah berjalan untuk satu tempoh masa, didapati bahawa masa log keluar semakin perlahan dan semakin perlahan

Masalah: Melalui penyahpepijatan, didapati bahawa memakan masa adalah terutamanya dalam tempoh pemadaman token

tokenStore.removeAccessToken(token);

Sebab: Semakin masa berlalu, semakin banyak dan lebih banyak token dan jadual storan token oauth_access_token menjadi luar biasa besar, jadi kecekapan pemadaman sangat lemah

Penyelesaian: Gunakan TokenStore lain, atau kosongkan data jadual oauth_access_token

Atas ialah kandungan terperinci Cara SpringBoot Security melaksanakan log keluar tunggal dan mengosongkan semua token. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:yisu.com. Jika ada pelanggaran, sila hubungi admin@php.cn Padam