Spring 安全 JWT 刷新令牌未过期

Spring security JWT refresh token not expiring

我是 spring 的新手,我正在研究具有 spring 安全性的 spring 引导 REST,目前我实现了 JWT 令牌。我有一些问题,但似乎找不到答案。我尝试添加刷新令牌。
起初我以为我会把它和用户一起存储在数据库中,但是 spring 安全性会自动执行所有操作,我似乎无法找到如何将它存储在 table 用户的给定字段中。
因此,继续前进我决定我将尝试坚持使用 spring 安全自动化并将刷新令牌过期时间设置为 10 秒以测试它是否过期,但遗憾的是它没有按预期工作 - 我可以使用刷新令牌作为只要我想要并用它生成新的令牌。
所以在这里我有几个问题:
1. 如何让刷新令牌在给定时间后过期?这是我的安全配置

@Configuration
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

@Value("${security.signing-key}")
private String signingKey;

@Value("${security.encoding-strength}")
private Integer encodingStrength;

@Value("${security.security-realm}")
private String securityRealm;

@Autowired
private UserDetailsService userDetailsService;

@Bean
@Override
protected AuthenticationManager authenticationManager() throws Exception {
    return super.authenticationManager();
}

@Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
    auth.userDetailsService(userDetailsService).passwordEncoder(passwordEncoder());
}

@Bean
public PasswordEncoder passwordEncoder() {
    PasswordEncoder encoder = new BCryptPasswordEncoder();
    return encoder;
}

@Override
protected void configure(HttpSecurity http) throws Exception {
    http.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS).and().httpBasic()
            .realmName(securityRealm).and().csrf().disable();

}

@Bean
public JwtAccessTokenConverter accessTokenConverter() {
    JwtAccessTokenConverter converter = new JwtAccessTokenConverter();
    converter.setSigningKey(signingKey);
    return converter;
}

@Bean
public TokenStore tokenStore() {
    return new JwtTokenStore(accessTokenConverter());
}

@Bean
@Primary
public DefaultTokenServices tokenServices() {
    DefaultTokenServices defaultTokenServices = new DefaultTokenServices();
    defaultTokenServices.setTokenStore(tokenStore());
    defaultTokenServices.setSupportRefreshToken(true);
    defaultTokenServices.setRefreshTokenValiditySeconds(10);
    return defaultTokenServices;
}

}

  1. 是否可以将刷新令牌传递给数据库并手动检查令牌是否有效,因为那是我的第一个想法。

我确实找到了答案,只是忘了更新我的工单。所以在这里,默认情况下 JwtTokenStore 不支持刷新令牌。 Here's JwtTokenStore source code。 所以这意味着,在设置中启用令牌实际上不会使其工作。我所做的是创建我自己的 JWT 令牌存储,它扩展了 JwtTokenStore 并编写了我自己的刷新令牌逻辑。

public class MyJwtTokenStore extends JwtTokenStore {

@Autowired
UserRepository userRepository;

public MyJwtTokenStore(JwtAccessTokenConverter jwtTokenEnhancer) {
    super(jwtTokenEnhancer);
}

@Override
public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) {
    String username = authentication.getUserAuthentication().getName();
    User user = userRepository.findByEmail(username);
    user.setToken(refreshToken.getValue());
    userRepository.save(user);
}

@Override
public OAuth2RefreshToken readRefreshToken(String token) {
    OAuth2Authentication authentication = super.readAuthentication(token);
    String username = authentication.getUserAuthentication().getName();
    User user = userRepository.findByEmail(username);
    if (!token.equals(user.getToken())) {
        return null;
    }
    OAuth2RefreshToken refreshToken = new DefaultOAuth2RefreshToken(token);
    return refreshToken;
}

@Override
public void removeRefreshToken(OAuth2RefreshToken token) {
    OAuth2Authentication authentication = super.readAuthentication(token.getValue());
    String username = authentication.getUserAuthentication().getName();
    User user = userRepository.findByEmail(username);
    user.setToken(null);
    userRepository.save(user);
}

}

在此之后,我刚刚更新了我的 TokenStore Bean

@Bean
public TokenStore tokenStore() {
    MyJwtTokenStore jwtTokenStore = new MyJwtTokenStore(accessTokenConverter());
    return jwtTokenStore;
    // return new JwtTokenStore(accessTokenConverter());
}

最后,我将其余设置添加到我的 AuthorizationServerConfigurerAdapter class 以支持刷新令牌并为其提供有效时间。

@Configuration
@EnableAuthorizationServer
public class AuthorizationServerConfig extends AuthorizationServerConfigurerAdapter {
@Value("${security.jwt.client-id}")
private String clientId;

@Value("${security.jwt.client-secret}")
private String clientSecret;

@Value("${security.jwt.grant-type}")
private String grantType;

@Value("${security.jwt.grant-type-other}")
private String grantTypeRefresh;

@Value("${security.jwt.scope-read}")
private String scopeRead;

@Value("${security.jwt.scope-write}")
private String scopeWrite = "write";

@Value("${security.jwt.resource-ids}")
private String resourceIds;

@Autowired
private TokenStore tokenStore;

@Autowired
private JwtAccessTokenConverter accessTokenConverter;

@Autowired
private AuthenticationManager authenticationManager;

@Autowired
private AppUserDetailsService userDetailsService;

@Autowired
private PasswordEncoder passwordEncoder;


@Override
public void configure(ClientDetailsServiceConfigurer configurer) throws Exception {
    configurer.inMemory()
            .withClient(clientId)
            .secret(passwordEncoder.encode(clientSecret))
            .authorizedGrantTypes(grantType, grantTypeRefresh).scopes(scopeRead, scopeWrite)
            .resourceIds(resourceIds).autoApprove(false).accessTokenValiditySeconds(1800) // 30min
            .refreshTokenValiditySeconds(86400); //24 hours
}

@Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
    TokenEnhancerChain enhancerChain = new TokenEnhancerChain();
    enhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter));
    endpoints.tokenStore(tokenStore).accessTokenConverter(accessTokenConverter).tokenEnhancer(enhancerChain)
            .reuseRefreshTokens(false).authenticationManager(authenticationManager)
            .userDetailsService(userDetailsService);
}

@Bean
public FilterRegistrationBean corsFilter() {
    UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
    CorsConfiguration config = new CorsConfiguration();
    config.setAllowCredentials(true);
    config.addAllowedOrigin("*"); // http://localhost:4200
    config.addAllowedHeader("*");
    config.addAllowedMethod("*");
    source.registerCorsConfiguration("/**", config);
    FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source));
    bean.setOrder(Ordered.HIGHEST_PRECEDENCE);
    return bean;
}

}