You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot Security资源服务器:JWT验证失败时如何刷新JWK集

解决方案

核心思路

当JWT因签名密钥不在本地JWK缓存导致验证失败时,主动触发JWK集刷新,再重新尝试验证当前JWT一次,避免直接返回401错误。

实现步骤

1. 自定义可手动刷新的JWK源

封装默认Nimbus JWK源,添加缓存和手动刷新逻辑,保证线程安全:

import com.nimbusds.jose.jwk.JWKSet
import com.nimbusds.jose.jwk.source.JWKSource
import com.nimbusds.jose.proc.SecurityContext
import java.util.concurrent.locks.ReentrantLock

class RefreshableJwkSource(private val delegate: JWKSource<SecurityContext>) : JWKSource<SecurityContext> {
    private val lock = ReentrantLock()
    private var cachedJwkSet: JWKSet? = null

    override fun getJWKs(context: SecurityContext): JWKSet {
        return cachedJwkSet ?: delegate.getJWKs(context).also { cachedJwkSet = it }
    }

    fun refresh() {
        lock.lock()
        try {
            cachedJwkSet = delegate.getJWKs(SecurityContext.NULL)
        } finally {
            lock.unlock()
        }
    }
}

2. 构建带重试逻辑的JwtDecoder

捕获JWT签名/密钥相关验证异常,触发JWK刷新后重新验证:

import org.springframework.security.oauth2.jwt.Jwt
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.JwtValidationException

class RetryOnRefreshJwtDecoder(
    private val decoderFactory: () -> JwtDecoder,
    private val refreshableJwkSource: RefreshableJwkSource
) : JwtDecoder {
    private var delegateDecoder: JwtDecoder = decoderFactory()

    override fun decode(token: String): Jwt {
        return try {
            delegateDecoder.decode(token)
        } catch (ex: JwtValidationException) {
            // 仅针对签名/密钥相关异常触发刷新,过滤过期、格式错误等无效请求
            if (ex.message?.contains("signature") == true || ex.message?.contains("key") == true) {
                refreshableJwkSource.refresh()
                delegateDecoder = decoderFactory()
                delegateDecoder.decode(token)
            } else {
                throw ex
            }
        }
    }
}

3. 替换Security配置中的默认JwtDecoder

将自定义组件注入Spring Security,替换默认实现:

import com.nimbusds.jose.jwk.source.JWKSource
import com.nimbusds.jose.proc.SecurityContext
import org.springframework.beans.factory.annotation.Value
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
import org.springframework.security.config.annotation.web.builders.HttpSecurity
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity
import org.springframework.security.oauth2.jwt.JwtDecoder
import org.springframework.security.oauth2.jwt.NimbusJwtDecoder
import org.springframework.security.web.SecurityFilterChain

@Configuration
@EnableWebSecurity
class MyCustomSecurityConfiguration {

    @Value("\${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}")
    private lateinit var jwkSetUri: String

    @Bean
    fun refreshableJwkSource(): RefreshableJwkSource {
        val defaultJwkSource = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).jwkSource
        return RefreshableJwkSource(defaultJwkSource)
    }

    @Bean
    fun jwtDecoder(refreshableJwkSource: RefreshableJwkSource): JwtDecoder {
        val decoderFactory = {
            NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
                .jwkSource(refreshableJwkSource)
                .build()
        }
        return RetryOnRefreshJwtDecoder(decoderFactory, refreshableJwkSource)
    }

    @Bean
    fun filterChain(http: HttpSecurity, jwtDecoder: JwtDecoder): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2ResourceServer {
                jwt {
                    decoder = jwtDecoder
                }
            }
        }
        return http.build()
    }
}

4. 保留原YAML配置

原配置无需修改,继续使用:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          jwk-set-uri: https://idp.example.com/.well-known/jwks.json

关键说明

  • 精准触发刷新:仅在签名/密钥验证失败时执行刷新,避免对过期、格式错误等无效请求做无意义重试。
  • 线程安全保障:使用ReentrantLock确保JWK刷新操作的原子性,防止并发刷新导致的资源冲突。
  • Decoder重建:每次刷新JWK后重新构建JwtDecoder,确保验证逻辑使用最新的密钥集。

内容的提问来源于stack exchange,提问作者scre_www

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.02 04:52:38