如何在spring-security 5.2中增加RemoteJWKSet缓存TTL

kg7wmglp  于 2022-11-11  发布在  Spring
关注(0)|答案(3)|浏览(343)

我们使用spring-security 5.2通过JWT验证来保护REST API。
通过spring:security:oauth2:resourceserver:jwt:jwk-set-uri属性,我们指示远程JWKS终结点,该终结点转换为Spring,并基于此URI创建NimbusJwtDecoder。再向下,创建RemoteJWKSet对象,该对象缓存对JWKS终结点的调用,默认TTL为5分钟。
有没有办法增加这个TTL以最小化远程调用?也许用不同的TTL在某处注入一个新的DefaultJWKSetCache示例?将其尽可能长时间地保存在缓存中似乎是安全的,因为当我们收到一个带有未知孩子的令牌时,对JWKS端点的调用将继续更新密钥集。
用于检索密钥的调用堆栈如下

JwtAuthenticationProvider
  public Authentication authenticate(Authentication authentication)
    ...
      jwt = this.jwtDecoder.decode(bearer.getToken())
    ...

o.s.security.oauth2.jwt.NimbusJwtDecoder
    public Jwt decode(String token)
    ...
      Jwt createdJwt = createJwt(token, jwt);
    ...

    private Jwt createJwt(String token, JWT parsedJwt)
    ...
      JWTClaimsSet jwtClaimsSet = this.jwtProcessor.process(parsedJwt, null);
    ....

DefaultJWTProcessor
      public JWTClaimsSet process(final JWT jwt, final C context)
        ...
          if (jwt instanceof SignedJWT) {
                return process((SignedJWT)jwt, context);
                }
        ...

      public JWTClaimsSet process(final SignedJWT signedJWT, final C context)
            ...
              List<? extends Key> keyCandidates = selectKeys(signedJWT.getHeader(), claimsSet, context);
          ...

      private List<? extends Key> selectKeys(final JWSHeader header, final JWTClaimsSet claimsSet, final C context)
        ....
          if (getJWSKeySelector() != null) {
                 return getJWSKeySelector().selectJWSKeys(header, context);
                 }      
        ....  

JWSVerificationKeySelector
  public List<Key> selectJWSKeys(final JWSHeader jwsHeader, final C context)
    ...
      List<JWK> jwkMatches = getJWKSource().get(new JWKSelector(jwkMatcher), context);
    ...

RemoteJWKSet
  public List<JWK> get(final JWKSelector jwkSelector, final C context)
  ...
    JWKSet jwkSet = jwkSetCache.get();
        if (jwkSet == null) {
            jwkSet = updateJWKSetFromURL();
        }
  ...

DefaultJWKSetCache  
  public JWKSet get() {

    if (isExpired()) {
      jwkSet = null; // clear
    }

    return jwkSet;
  }

安全性相依性:

+- org.springframework.boot:spring-boot-starter-security:jar:2.2.4.RELEASE:compile
|  +- org.springframework.security:spring-security-config:jar:5.2.1.RELEASE:compile
|  \- org.springframework.security:spring-security-web:jar:5.2.1.RELEASE:compile
+- org.springframework.security:spring-security-oauth2-jose:jar:5.2.2.RELEASE:compile
|  +- org.springframework.security:spring-security-core:jar:5.2.1.RELEASE:compile
|  \- org.springframework.security:spring-security-oauth2-core:jar:5.2.1.RELEASE:compile
+- com.nimbusds:nimbus-jose-jwt:jar:8.8:compile
|  +- com.github.stephenc.jcip:jcip-annotations:jar:1.0-1:compile
|  \- net.minidev:json-smart:jar:2.3:compile (version selected from constraint [1.3.1,2.3])
|     \- net.minidev:accessors-smart:jar:1.2:compile
|        \- org.ow2.asm:asm:jar:5.0.4:compile
+- org.springframework.security:spring-security-oauth2-resource-server:jar:5.2.1.RELEASE:compile
t3psigkw

t3psigkw1#

看起来我来得有点晚了,但是我是为5.4版本实现这个特性的人,现在您可以使用Spring Cache配置它:

var jwkSetCache = new ConcurrentMapCache("jwkSetCache", CacheBuilder.newBuilder()
    // can set the value here or better populate from properties
    .expireAfterWrite(Duration.ofMinutes(30))
    .build().asMap(), false);
var decoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
    .restOperations(restOperations)
    .cache(jwkSetCache)
    .build();
xlpyo6sf

xlpyo6sf2#

最后,我做了以下工作:

@Bean
    public JwtDecoder jwtDecoder() {
        JWSKeySelector<SecurityContext> jwsKeySelector = null;
        try {
            URL jwksUrl = new URL("https://localhost/.well-known/openid-configuration/jwks");
            long cacheLifespan = 500;
            long refreshTime = 400;
            JWKSetCache jwkSetCache = new DefaultJWKSetCache(cacheLifespan, refreshTime, TimeUnit.MINUTES);
            RemoteJWKSet<SecurityContext> jwkSet = new RemoteJWKSet<>(jwksUrl,null,jwkSetCache);
            jwsKeySelector = JWSAlgorithmFamilyJWSKeySelector.fromJWKSource(jwkSet);
        }
        catch (KeySourceException e) {
            e.printStackTrace();
        }
        catch (MalformedURLException e) {
            e.printStackTrace();
        }

        DefaultJWTProcessor<SecurityContext> jwtProcessor = new DefaultJWTProcessor<>();
        jwtProcessor.setJWSKeySelector(jwsKeySelector);

        return new NimbusJwtDecoder(jwtProcessor);
    }
zpjtge22

zpjtge223#

Spring Security 5.4允许传递一个缓存给decoderbuilder方法,所以你可以传递你自己的缓存,nimbusjwtdecoder将使用这个缓存来获取值。
要清除缓存,您可以在配置中设置一个调度程序作业。

@Scheduled(fixedRateString = "5000")
    public void clearCachesAfterEvictionTime() {
        Optional.ofNullable(cacheManager().getCache("JWKSetCache")).ifPresent(Cache::clear);
    }

希望能有所帮助。

相关问题