使用刷新令牌时 Spring Boot JWT 令牌无效签名

Posted

技术标签:

【中文标题】使用刷新令牌时 Spring Boot JWT 令牌无效签名【英文标题】:Spring Boot JWT tokens invalid signature when using refrest token 【发布时间】:2020-02-12 07:35:23 【问题描述】:

所以我正在构建一个 OAuth 服务器。

我可以正常访问我的授权服务器,但是签名似乎无效。

将其输入https://jwt.io/ 会产生无效签名。更糟糕的是,当我尝试使用刷新令牌时,我得到 InvalidTokenException,无法将访问令牌转换为 JSON。

这个秘密来自JwtAccessTokenConverter.java

protected Map<String, Object> decode(String token) 
        try 
            Jwt jwt = JwtHelper.decodeAndVerify(token, verifier);
            String claimsStr = jwt.getClaims();
            Map<String, Object> claims = objectMapper.parseMap(claimsStr);
            if (claims.containsKey(EXP) && claims.get(EXP) instanceof Integer) 
                Integer intValue = (Integer) claims.get(EXP);
                claims.put(EXP, new Long(intValue));
            
            this.getJwtClaimsSetVerifier().verify(claims);
            return claims;
        
        catch (Exception e) 
            throw new InvalidTokenException("Cannot convert access token to JSON", e);
        
    

真正的例外是计算的签名与给定的签名不匹配。因此,由于某种原因,签名实际上并未签署所有内容。我有一个自定义令牌增强器,它放置了几个额外的值。我怀疑这没有得到签名,我不知道为什么。

下面是我的授权服务器

@Configuration
@EnableAuthorizationServer
@EnableConfigurationProperties(SecurityProperties::class)
class AuthorizationServerConfiguration(private val dataSource: DataSource, private val passwordEncoder: PasswordEncoder,
                                       private val authenticationManager: AuthenticationManager, private val securityProperties: SecurityProperties,
                                       private val userDetailsService: UserDetailsService) : AuthorizationServerConfigurerAdapter() 

    private var jwtAccessTokenConverter: JwtAccessTokenConverter? = null
    private var tokenStore: TokenStore? = null

    @Bean
    fun tokenEnhancer(): CustomTokenEnhancer 
        return CustomTokenEnhancer()
    

    @Bean
    fun loggingExceptionTranslator(): DefaultWebResponseExceptionTranslator 
        return object : DefaultWebResponseExceptionTranslator() 
            @Throws(Exception::class)
            override fun translate(e: Exception): ResponseEntity<OAuth2Exception> 
                // This is the line that prints the stack trace to the log. You can customise this to format the trace etc if you like
                e.printStackTrace()

                // Carry on handling the exception
                val responseEntity = super.translate(e)
                val headers = HttpHeaders()
                headers.setAll(responseEntity.headers.toSingleValueMap())
                val excBody = responseEntity.body
                return ResponseEntity(excBody, headers, responseEntity.statusCode)
            
        
    

    @Bean
    fun tokenStore(): TokenStore 
        if (tokenStore == null) 
            tokenStore = JwtTokenStore(tokenEnhancer())
        
        return tokenStore as TokenStore
    

    @Bean
    fun tokenServices(tokenStore: TokenStore,
                      clientDetailsService: ClientDetailsService): DefaultTokenServices 
        val tokenServices = DefaultTokenServices()
        tokenServices.setSupportRefreshToken(true)
        tokenServices.setTokenStore(tokenStore)
        tokenServices.setAuthenticationManager(this.authenticationManager)
        tokenServices.setAccessTokenValiditySeconds(securityProperties.tokenTimeToLive)
        return tokenServices
    

    @Bean
    fun jwtAccessTokenConverter(): JwtAccessTokenConverter 
        if (jwtAccessTokenConverter != null) 
            return jwtAccessTokenConverter as JwtAccessTokenConverter
        

        val jwtProperties = securityProperties.jwt
        val keyPair = jwtProperties?.let  keyStoreKeyFactory(it) ?.let  keyPair(jwtProperties, it) 
        jwtAccessTokenConverter = CustomTokenEnhancer()
        jwtAccessTokenConverter!!.setKeyPair(keyPair!!)
        jwtAccessTokenConverter!!.afterPropertiesSet()
        return jwtAccessTokenConverter as JwtAccessTokenConverter
    

    @Throws(Exception::class)
    override fun configure(clients: ClientDetailsServiceConfigurer) 
        clients.jdbc(this.dataSource)
    

    override fun configure(endpoints: AuthorizationServerEndpointsConfigurer) 
        endpoints.authenticationManager(this.authenticationManager)
                .accessTokenConverter(jwtAccessTokenConverter())
                .userDetailsService(this.userDetailsService)
                .tokenStore(tokenStore())
    

    override fun configure(oauthServer: AuthorizationServerSecurityConfigurer) 
        oauthServer.passwordEncoder(this.passwordEncoder).tokenKeyAccess("permitAll()")
                .checkTokenAccess("isAuthenticated()")
    

    private fun keyPair(jwtProperties: SecurityProperties.JwtProperties?, keyStoreKeyFactory: KeyStoreKeyFactory): KeyPair 
        return keyStoreKeyFactory.getKeyPair(jwtProperties?.keyPairAlias, jwtProperties?.keyPairPassword?.toCharArray())
    

    private fun keyStoreKeyFactory(jwtProperties: SecurityProperties.JwtProperties): KeyStoreKeyFactory 
        return KeyStoreKeyFactory(jwtProperties.keyStore, jwtProperties.keyStorePassword?.toCharArray())
    

【问题讨论】:

【参考方案1】:

我需要在以下行中使用 bean

@Bean
fun tokenStore(): TokenStore 
    return JwtTokenStore(jwtAccessTokenConverter())

【讨论】:

以上是关于使用刷新令牌时 Spring Boot JWT 令牌无效签名的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot JWT - 如何实现刷新令牌和注销 REST-API

Spring Boot 安全性 - 允许使用过期 JWT 令牌的用户请求

Spring Boot 中 JWT 的最佳实践是啥?

如何在 Spring Boot 中实现刷新令牌

如何使用 JWT 在 Spring Boot 中获取 Refresh Token

如何在使用 Spring Boot 的 JWT 令牌时禁用同一用户帐户的多个登录