spring-security Auth 0-无法检索远程JWK集:读取超时

nimxete2  于 2022-11-11  发布在  Spring
关注(0)|答案(5)|浏览(299)

I'm suffering this one: Couldn't retrieve remote JWK set: Read timed out
I am using Java 11 with Spring boot 2.5.3,
for dependencies:

  • spring-security-oauth2-jose 5.5.1,
  • spring-boot-starter-oauth2-client
  • spring-boot-starter-security

Auth0 as users provider.
Any clue ?

...
Caused by: java.lang.IllegalStateException: com.nimbusds.jose.RemoteKeySourceException: Couldn't retrieve remote JWK set: Read timed out
    at org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils.getSignatureAlgorithms(JwtDecoderProviderConfigurationUtils.java:107) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.security.oauth2.jwt.ReactiveJwtDecoders.withProviderConfiguration(ReactiveJwtDecoders.java:120) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.security.oauth2.jwt.ReactiveJwtDecoders.fromIssuerLocation(ReactiveJwtDecoders.java:100) ~[spring-security-oauth2-jose-5.5.1.jar:5.5.1]
    at org.springframework.boot.autoconfigure.security.oauth2.resource.reactive.ReactiveOAuth2ResourceServerJwkConfiguration$JwtConfiguration.jwtDecoderByIssuerUri(ReactiveOAuth2ResourceServerJwkConfiguration.java:95) ~[spring-boot-autoconfigure-2.5.3.jar:2.5.3]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na]
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na]
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na]
    at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na]
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154) ~[spring-beans-5.3.9.jar:5.3.9]
qybjjes1

qybjjes11#

我发现有时候我仍然会收到Auth 0的超时。看起来他们对JWK请求的响应太慢了,需要更多的等待时间。在我的项目中,导致它失败的超时是com. nimbusds. jose. jwk. source. RemoteJWKSet中定义的两个常量。

public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 500;
public static final int DEFAULT_HTTP_READ_TIMEOUT = 500;

所以我提出了一个非常丑陋的解决方案,它对我很有效,直到有人把这些常数变成了属性。
假设您使用的是Auth 0指南https://auth0.com/docs/quickstart/backend/java-spring-security5/01-authorization,并且您添加了“jwk-set-uri:https://your-domain/.well-known/jwks.json”键,因为com.nimbusds.jose.jwk.source.RemoteJWKSet不是一个我可以用自己的自定义实现来替换的bean,或者不能使用AOP来替换在构造函数中传递的超时参数,所以我不得不复制一些东西来制作我自己版本的所需类。
解决方案
我制作了以下类的自己的版本,只是从原始代码中复制了代码,并在需要的地方进行了定制:

  • RemoteJWKSet(自定义增加超时常量)
public static final int DEFAULT_HTTP_CONNECT_TIMEOUT = 1000;
    public static final int DEFAULT_HTTP_READ_TIMEOUT = 1000;
  • jwt. JwtDecoders(改为创建RemoteJWKSet的自定义版本的示例)
private static JwtDecoder withProviderConfiguration(Map<String, Object> configuration, String issuer) {
        CustomJwtDecoderProviderConfigurationUtils.validateIssuer(configuration, issuer);
        OAuth2TokenValidator<Jwt> jwtValidator = JwtValidators.createDefaultWithIssuer(issuer);
        String jwkSetUri = configuration.get("jwks_uri").toString();
        CustomRemoteJWKSet<SecurityContext> jwkSource = new CustomRemoteJWKSet<>(url(jwkSetUri));
        Set<SignatureAlgorithm> signatureAlgorithms = CustomJwtDecoderProviderConfigurationUtils
                .getSignatureAlgorithms(jwkSource);
        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
                .jwsAlgorithms((algs) -> algs.addAll(signatureAlgorithms)).build();
        jwtDecoder.setJwtValidator(jwtValidator);
        return jwtDecoder;
    }
  • org.springframework.security.oauth2.jwt.JwtDecoderProviderConfigurationUtils(这没有自定义,但我不得不复制它,因为它在其包外不可见)

然后,我将配置中的解码器替换为自定义解码器

@Bean
public JwtDecoder jwtDecoder() {
    NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
            CustomJwtDecoders.fromOidcIssuerLocation(issuer);

    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
    OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
    OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(
        withIssuer, audienceValidator);

    jwtDecoder.setJwtValidator(withAudience);

    return jwtDecoder;
}

到目前为止,一切顺利,没有更多的暂停。

zzoitvuj

zzoitvuj2#

这个答案和Juan的答案的主要区别在于,我使用的不是RemoteJWKSet,而是DefaultResourceRetriever,我还必须复制并粘贴JwtDecoderProviderConfigurationUtils作为JwtUtil,以访问处理OAuth DOM的helper函数。
真痛苦

@Value("${auth0.audience}")
  private String audience;

  @Value("${spring.security.oauth2.resourceserver.jwt.issuer-uri}")
  private String issuer;

  @Bean
  JwtDecoder jwtDecoder(RestTemplateBuilder builder, CacheManager cacheManager) throws MalformedURLException {

    RestOperations restOperations = builder
        .setConnectTimeout(Duration.ofSeconds(60))
        .setReadTimeout(Duration.ofSeconds(60))
        .build();

    // JwtUtil is a copy and paste of JwtDecoderProviderConfigurationUtils in order to be able to
    // call a couple of functions. The Spring class is inconveniently package protected.
    Map<String, Object> configuration = JwtUtil.getConfigurationForIssuerLocation(issuer);
    String jwkSetUri = configuration.get("jwks_uri").toString();
    RemoteJWKSet<SecurityContext> jwkSource = new RemoteJWKSet<>(new URL(jwkSetUri), new DefaultResourceRetriever());
    NimbusJwtDecoder nimbusDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri)
        .jwsAlgorithms((algos) -> algos.addAll(JwtUtil.getSignatureAlgorithms(jwkSource)))
        .restOperations(restOperations)
        .cache(Objects.requireNonNull(cacheManager.getCache("auth")))
        .build();

    OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
    OAuth2TokenValidator<Jwt> withAudience =
        new DelegatingOAuth2TokenValidator<>(JwtValidators.createDefaultWithIssuer(issuer), audienceValidator);

    nimbusDecoder.setJwtValidator(withAudience);

    return nimbusDecoder;
  }

YAML语言

auth0:
  # Replace with the API Identifier for your Auth0 API.
  audience: https://myapp.eu.auth0.com/api/v2/

# The following is standard Spring Security OAuth2 configuration.

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          # Replace with the domain of your Auth0 tenant.
          # Note the trailing slash is important!
          issuer-uri: https://myapp.eu.auth0.com/
sqxo8psd

sqxo8psd3#

您可以在为JwtDecoder创建Bean时配置自定义超时,如下所示:

@Bean
public JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
    RestOperations rest = builder
            .setConnectionTimeout(60000)
            .setReadTimeout(60000)
            .build();

    NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build();
    return jwtDecoder;
}

来源- Spring安全性文件:https://docs.spring.io/spring-security/site/docs/5.2.12.RELEASE/reference/html/oauth2.html#oauth2resourceserver-jwt-timeouts
在Auth 0配置的情况下,jwtDecoder Bean如下所示:

@Bean
    JwtDecoder jwtDecoder(RestTemplateBuilder builder) {
        RestOperations rest = builder
                .setConnectTimeout(Duration.ofSeconds(10))
                .setReadTimeout(Duration.ofSeconds(10))
                .build();

        NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withJwkSetUri(jwkSetUri).restOperations(rest).build();

        OAuth2TokenValidator<Jwt> audienceValidator = new AudienceValidator(audience);
        OAuth2TokenValidator<Jwt> withIssuer = JwtValidators.createDefaultWithIssuer(issuer);
        OAuth2TokenValidator<Jwt> withAudience = new DelegatingOAuth2TokenValidator<>(withIssuer, audienceValidator);

        jwtDecoder
                .setJwtValidator(withAudience);

        return jwtDecoder;
    }
m528fe3b

m528fe3b4#

我遇到了同样的问题,似乎它无法从已知配置中获取jwk集。我仍然需要找出这是我的项目中的问题还是Auth 0中的问题。尝试在您的属性中显式添加它,之后它对我有效。我的如下所示:

spring:
  security:
    oauth2:
      resourceserver:
        jwt:
          issuer-uri: https://your-domain/
          jwk-set-uri: https://your-domain/.well-known/jwks.json

另外,spring-boot-starter-oauth2-resource-server依赖关系应该足以完成这一任务。

j7dteeu8

j7dteeu85#

我发现我的auth0租户托管在不同的位置(在美国,尽管我住在欧洲),所以正如第一个答案中所建议的,阅读超时约束太小。在我在不同的地区创建了一个新租户后,这个问题就消失了。

相关问题