>Java >java지도 시간 >SpringBoot Security가 싱글 사인아웃을 구현하고 모든 토큰을 지우는 방법

SpringBoot Security가 싱글 사인아웃을 구현하고 모든 토큰을 지우는 방법

WBOY
WBOY앞으로
2023-05-14 13:43:112080검색

요구사항

  • A, B, C 시스템은 sso 서비스를 통해 로그인

  • A, B, C 시스템은 각각 Atoken, Btoken, Ctoken 3개의 토큰을 얻습니다

  • 시스템 중 하나 이후 적극적으로 로그아웃합니다. 다른 두 시스템도 로그아웃되었습니다

  • 이제 모든 Atoken, Btoken 및 Ctoken이 유효하지 않습니다

Record token

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>

Token 저장소 클래스는 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

  • 를 생성하고 AuthJdbcTokenStore

    로 전달해야 하므로 DataSource를 도입하세요.
  • tokenServices TokenStore 추가

  • endpoints 추가 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

  • inherits SimpleUrlLogoutSuccessHandler

  • 사용자 이름 userName

  • 가져오기

    로그인 시 Redis에 저장된 토큰 목록 가져오기

  • token 문자열은 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 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 yisu.com에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제