跳到主要内容
版本:7.0.2

OAuth 2.0 资源服务器 JWT

DeepSeek V3 中英对照 JWT OAuth 2.0 Resource Server JWT

JWT 的最小依赖​

大多数资源服务器的支持功能都集成在 spring-security-oauth2-resource-server 中。然而,对 JWT 的解码和验证支持位于 spring-security-oauth2-jose 模块中,这意味着要构建一个支持 JWT 编码的 Bearer Token 的资源服务器,两者都是必需的。

JWT 的最小配置​

在使用 Spring Boot 时,将应用程序配置为资源服务器包含两个基本步骤。首先,包含所需的依赖项;其次,指定授权服务器的位置。

指定授权服务器​

在Spring Boot应用中,要指定使用哪个授权服务器,只需执行以下操作:

spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com/issuer

其中 [idp.example.com/issuer](https://idp.example.com/issuer) 是授权服务器将颁发的 JWT 令牌中 iss 声明所包含的值。资源服务器将使用此属性进行进一步的自配置、发现授权服务器的公钥,并随后验证传入的 JWT。

备注

要使用 issuer-uri 属性,还必须满足以下条件之一:[idp.example.com/issuer/.well-known/openid-configuration](https://idp.example.com/issuer/.well-known/openid-configuration)、[idp.example.com/.well-known/openid-configuration/issuer](https://idp.example.com/.well-known/openid-configuration/issuer) 或 [idp.example.com/.well-known/oauth-authorization-server/issuer](https://idp.example.com/.well-known/oauth-authorization-server/issuer) 是授权服务器支持的端点。此端点被称为 Provider Configuration 端点或 Authorization Server Metadata 端点。

就是这样!

启动预期​

当使用此属性及这些依赖项时,资源服务器将自动配置自身以验证JWT编码的Bearer令牌。

它通过一个确定性的启动过程实现这一点:

  1. 查询提供者配置或授权服务器元数据端点以获取 jwks_url 属性

  2. 查询 jwks_url 端点以获取支持的算法

  3. 配置验证策略,使其查询 jwks_url 以获取所发现算法的有效公钥

  4. 配置验证策略,使其针对 [idp.example.com](https://idp.example.com) 验证每个 JWT 的 iss 声明

这一过程的一个结果是,授权服务器必须处于运行状态并能够接收请求,资源服务器才能成功启动。

备注

如果资源服务器在查询授权服务器时,授权服务器处于宕机状态(在适当的超时设置下),那么启动将会失败。

运行时预期​

一旦应用程序启动,资源服务器将尝试处理任何包含 Authorization: Bearer 头的请求:

GET / HTTP/1.1
Authorization: Bearer some-token-value # Resource Server will process this

只要此方案被指定,资源服务器将尝试根据Bearer Token规范处理请求。

给定一个格式正确的JWT,资源服务器将:

  1. 使用启动时从 jwks_url 端点获取并与 JWT 匹配的公钥验证其签名

  2. 验证 JWT 的 exp 和 nbf 时间戳以及 JWT 的 iss 声明,并且

  3. 将每个 scope 映射到一个带有 SCOPE_ 前缀的权限。

备注

随着授权服务器提供新的密钥,Spring Security 将自动轮换用于验证 JWT 的密钥。

默认情况下,生成的 Authentication#getPrincipal 是一个 Spring Security 的 Jwt 对象,而 Authentication#getName 则映射到 JWT 的 sub 属性(如果该属性存在的话)。

从这里开始,可以考虑跳转到:

JWT 认证的工作原理​

接下来,我们来看看 Spring Security 在基于 servlet 的应用程序(例如我们刚刚看到的那个)中用于支持 JWT 身份验证的架构组件。

让我们来看看 JwtAuthenticationProvider 在 Spring Security 中是如何工作的。该图详细解释了 读取承载令牌 章节图示中的 AuthenticationManager 是如何工作的。

jwtauthenticationprovider

图 1. JwtAuthenticationProvider 使用示例

1 来自读取承载令牌的身份验证 Filter 会将一个 BearerTokenAuthenticationToken 传递给由 ProviderManager 实现的 AuthenticationManager。

2 ProviderManager 被配置为使用类型为 JwtAuthenticationProvider 的 AuthenticationProvider。

3 JwtAuthenticationProvider 使用 JwtDecoder 对 Jwt 进行解码、验证和校验。

4 随后,JwtAuthenticationProvider 使用 JwtAuthenticationConverter 将 Jwt 转换为已授权权限的 Collection。

5 当认证成功时,返回的 Authentication 类型为 JwtAuthenticationToken,其主体(principal)是由配置的 JwtDecoder 返回的 Jwt,并且拥有一组权限(authorities),其中至少包含 FACTOR_BEARER。最终,返回的 JwtAuthenticationToken 将由认证 Filter 设置到 SecurityContextHolder 上。

直接指定授权服务器的JWK Set Uri​

如果授权服务器不支持任何配置端点,或者资源服务器必须能够独立于授权服务器启动,那么也可以提供 jwk-set-uri:

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

JWK Set URI 并非标准化配置,但通常可以在授权服务器的文档中找到

因此,资源服务器在启动时将不会对授权服务器执行ping操作。我们仍指定 issuer-uri,以便资源服务器继续验证传入JWT中的 iss 声明。

备注

此属性也可直接在 DSL 上提供。

提供受众群体​

正如之前所见,issuer-uri 属性用于验证 iss 声明;这指明了 JWT 的签发者。

Boot 还拥有 audiences 属性,用于验证 aud 声明;这表示 JWT 的接收方。

资源服务器的受众可以这样表示:

spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com
audiences: https://my-resource-server.example.com
备注

如有需要,你也可以通过编程方式添加 JWT 验证。

结果将是,如果JWT的iss声明不是[idp.example.com](https://idp.example.com),并且其aud声明列表中不包含[my-resource-server.example.com](https://my-resource-server.example.com),那么验证将失败。

覆盖或替换引导自动配置​

Spring Boot 会代表资源服务器生成两个 @Bean。

第一个是配置应用作为资源服务器的 SecurityFilterChain。当包含 spring-security-oauth2-jose 时,这个 SecurityFilterChain 的配置如下:

@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}

如果应用程序没有暴露 SecurityFilterChain bean,那么 Spring Boot 将会暴露上述默认的配置。

替换这个操作非常简单,只需在应用程序中暴露该bean即可:

import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;

@Configuration
@EnableWebSecurity
public class MyCustomSecurityConfiguration {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/messages/**").access(hasScope("message:read"))
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt((jwt) -> jwt
.jwtAuthenticationConverter(myConverter())
)
);
return http.build();
}
}

上述要求适用于任何以 /messages/ 开头的 URL,需要 message:read 权限范围。

oauth2ResourceServer DSL 上的方法同样会覆盖或替换自动配置。

例如,Spring Boot 创建的第二个 @Bean 是一个 JwtDecoder,它将字符串令牌解码为经过验证的 Jwt 实例:

@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromIssuerLocation(issuerUri);
}
备注

调用 JwtDecoders#fromIssuerLocation 会触发对 Provider Configuration 或 Authorization Server Metadata 端点的调用,以推导出 JWK Set URI。

如果应用程序没有暴露一个 JwtDecoder bean,那么 Spring Boot 将会暴露上述默认的 bean。

并且其配置可以通过 jwkSetUri() 方法进行覆盖,或使用 decoder() 方法进行替换。

或者,如果你完全没有使用Spring Boot,那么这两个组件——过滤器链和JwtDecoder——都可以在XML中进行配置。

过滤器链的配置方式如下:

<http>
<intercept-uri pattern="/**" access="authenticated"/>
<oauth2-resource-server>
<jwt decoder-ref="jwtDecoder"/>
</oauth2-resource-server>
</http>

而 JwtDecoder 则如下所示:

<bean id="jwtDecoder"
class="org.springframework.security.oauth2.jwt.JwtDecoders"
factory-method="fromIssuerLocation">
<constructor-arg value="${spring.security.oauth2.resourceserver.jwt.jwk-set-uri}"/>
</bean>

使用 jwkSetUri()​

授权服务器的JWK Set Uri可以通过配置属性进行设置,也可以在DSL中提供:

@Configuration
@EnableWebSecurity
public class DirectlyConfiguredJwkSetUri {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt((jwt) -> jwt
.jwkSetUri("https://idp.example.com/.well-known/jwks.json")
)
);
return http.build();
}
}

使用 jwkSetUri() 优先于任何配置属性。

使用 decoder()​

比 jwkSetUri() 更强大的是 decoder(),它将完全替换任何 Boot 对 JwtDecoder 的自动配置:

@Configuration
@EnableWebSecurity
public class DirectlyConfiguredJwtDecoder {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt((jwt) -> jwt
.decoder(myCustomDecoder())
)
);
return http.build();
}
}

这在需要更深入的配置时非常方便,例如验证、映射或请求超时等场景。

暴露 JwtDecoder @Bean​

或者,暴露一个 JwtDecoder @Bean 与使用 decoder() 方法具有相同的效果。你可以通过 jwkSetUri 来构建一个,如下所示:

@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
}

或者,您也可以使用颁发者,让 NimbusJwtDecoder 在调用 build() 时查找 jwkSetUri,如下所示:

@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(issuer).build();
}

或者,如果默认设置符合您的需求,也可以使用 JwtDecoders,它除了配置解码器的验证器外,还会执行上述操作:

@Bean
public JwtDecoders jwtDecoder() {
return JwtDecoders.fromIssuerLocation(issuer);
}

配置受信任算法​

默认情况下,NimbusJwtDecoder 以及资源服务器将仅信任并使用 RS256 算法验证令牌。

您可以通过 Spring Boot、NimbusJwtDecoder 构建器 或 JWK Set 响应 来自定义此配置。

通过 Spring Boot​

设置算法的最简单方式是将其作为属性:

spring:
security:
oauth2:
resourceserver:
jwt:
jws-algorithms: RS512
jwk-set-uri: https://idp.example.org/.well-known/jwks.json

使用构建器​

然而,为了获得更强大的功能,我们可以使用 NimbusJwtDecoder 自带的构建器:

@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).build();
}

多次调用 jwsAlgorithm 将配置 NimbusJwtDecoder 以信任多种算法,如下所示:

@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();
}

或者,你可以调用 jwsAlgorithms:

@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithms(algorithms -> {
algorithms.add(RS512);
algorithms.add(ES512);
}).build();
}

从 JWK 集响应​

由于 Spring Security 的 JWT 支持基于 Nimbus,您同样可以利用其所有强大功能。

例如,Nimbus 提供了一个 JWSKeySelector 实现,它会根据 JWK Set URI 的响应来选择算法集合。你可以像这样使用它来生成一个 NimbusJwtDecoder:

@Bean
public JwtDecoder jwtDecoder() {
// makes a request to the JWK Set endpoint
JWSKeySelector<SecurityContext> jwsKeySelector =
JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL(this.jwkSetUrl);

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

return new NimbusJwtDecoder(jwtProcessor);
}

信任单个非对称密钥​

相比通过JWK Set端点支持资源服务器,更简单的方法是硬编码一个RSA公钥。该公钥可以通过Spring Boot或使用构建器来提供。

通过 Spring Boot 配置​

通过Spring Boot指定密钥非常简单。密钥的位置可以这样指定:

spring:
security:
oauth2:
resourceserver:
jwt:
public-key-location: classpath:my-key.pub

或者,为了支持更复杂的查找,你可以对 RsaKeyConversionServicePostProcessor 进行后处理:

@Bean
BeanFactoryPostProcessor conversionServiceCustomizer() {
return beanFactory ->
beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)
.setResourceLoader(new CustomResourceLoader());
}

指定您的密钥位置:

key.location: hfds://my-key.pub

然后自动装配该值:

@Value("${key.location}")
RSAPublicKey key;

使用构建器​

要直接配置 RSAPublicKey,你可以简单地使用相应的 NimbusJwtDecoder 构建器,如下所示:

@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(this.key).build();
}

信任单个对称密钥​

使用单个对称密钥也很简单。你可以直接加载你的 SecretKey,并使用相应的 NimbusJwtDecoder 构建器,如下所示:

@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(this.key).build();
}

配置授权​

由 OAuth 2.0 授权服务器颁发的 JWT 通常包含 scope 或 scp 属性,用以表明其被授予的权限范围(或权限),例如:

{ …​, "scope" : "messages contacts"}

在这种情况下,资源服务器会尝试将这些作用域强制转换为已授予权限的列表,并在每个作用域前加上字符串"SCOPE_"。

这意味着,要使用从JWT派生的作用域来保护端点或方法,相应的表达式应包含此前缀:

import static org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;

@Configuration
@EnableWebSecurity
public class DirectlyConfiguredJwkSetUri {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.requestMatchers("/contacts/**").access(hasScope("contacts"))
.requestMatchers("/messages/**").access(hasScope("messages"))
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}

或者类似地,对于方法安全:

@PreAuthorize("hasAuthority('SCOPE_messages')")
public List<Message> getMessages(...) {}

手动提取权限​

然而,在某些情况下,这种默认处理方式是不够的。例如,有些授权服务器并不使用 scope 属性,而是使用它们自定义的属性。或者,在其他时候,资源服务器可能需要将某个属性或一组属性适配为内部化的权限。

为此,Spring Security 提供了 JwtAuthenticationConverter,它负责将 JWT 转换为 Authentication。默认情况下,Spring Security 会为 JwtAuthenticationProvider 装配一个默认的 JwtAuthenticationConverter 实例。

在配置 JwtAuthenticationConverter 时,你可以提供一个辅助转换器,用于将 Jwt 转换为已授权权限的 Collection。

假设你的授权服务器通过一个名为 authorities 的自定义声明来传递权限。在这种情况下,你可以配置 JwtAuthenticationConverter 应检查的声明,如下所示:

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");

JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}

您也可以配置不同的权限前缀。例如,可以将每个权限的前缀从 SCOPE_ 改为 ROLE_,具体操作如下:

@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");

JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}

或者,你也可以通过调用 JwtGrantedAuthoritiesConverter#setAuthorityPrefix("") 来完全移除前缀。

为了提供更大的灵活性,DSL支持完全替换转换器为任何实现了Converter<Jwt, AbstractAuthenticationToken>接口的类:

static class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
public AbstractAuthenticationToken convert(Jwt jwt) {
return new CustomAuthenticationToken(jwt);
}
}

// ...

@Configuration
@EnableWebSecurity
public class CustomAuthenticationConverterConfig {
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests((authorize) -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2
.jwt((jwt) -> jwt
.jwtAuthenticationConverter(new CustomAuthenticationConverter())
)
);
return http.build();
}
}

配置验证​

使用最小化Spring Boot配置,指定授权服务器的签发者URI后,资源服务器将默认验证iss声明以及exp和nbf时间戳声明。

在需要自定义验证的场景下,资源服务器内置了两个标准验证器,同时也支持自定义的 OAuth2TokenValidator 实例。

自定义时间戳验证​

JWT 通常具有一个有效时间窗口,其起始时间由 nbf 声明表示,结束时间由 exp 声明表示。

然而,每台服务器都可能出现时钟漂移,这可能导致令牌在一台服务器上显示为已过期,而在另一台服务器上却未过期。随着分布式系统中协作服务器数量的增加,这可能会给一些实现带来困扰。

资源服务器使用 JwtTimestampValidator 来验证令牌的有效时间窗口,并且可以通过配置 clockSkew 来缓解上述问题:

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromIssuerLocation(issuerUri);

OAuth2TokenValidator<Jwt> withClockSkew = new DelegatingOAuth2TokenValidator<>(
new JwtTimestampValidator(Duration.ofSeconds(60)),
new JwtIssuerValidator(issuerUri));

jwtDecoder.setJwtValidator(withClockSkew);

return jwtDecoder;
}
备注

默认情况下,Resource Server 配置了 60 秒的时钟偏移量。

配置 RFC 9068 验证​

如果你需要符合 RFC 9068 规范的令牌,可以通过以下方式配置验证:

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuerUri)
.validateTypes(false).build();
jwtDecoder.setJwtValidator(JwtValidators.createAtJwtValidator()
.audience("https://audience.example.org")
.clientId("client-identifier")
.issuer("https://issuer.example.org").build());
return jwtDecoder;
}

配置自定义验证器​

使用 OAuth2TokenValidator API 为 aud 声明 添加检查非常简单:

OAuth2TokenValidator<Jwt> audienceValidator() {
return new JwtClaimValidator<List<String>>(AUD, aud -> aud.contains("messaging"));
}

或者,为了获得更多控制权,你可以实现自己的 OAuth2TokenValidator:

static class AudienceValidator implements OAuth2TokenValidator<Jwt> {
OAuth2Error error = new OAuth2Error("custom_code", "Custom error message", null);

@Override
public OAuth2TokenValidatorResult validate(Jwt jwt) {
if (jwt.getAudience().contains("messaging")) {
return OAuth2TokenValidatorResult.success();
} else {
return OAuth2TokenValidatorResult.failure(error);
}
}
}

// ...

OAuth2TokenValidator<Jwt> audienceValidator() {
return new AudienceValidator();
}

接着,要将其添加到资源服务器中,只需指定 JwtDecoder 实例即可:

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
JwtDecoders.fromIssuerLocation(issuerUri);

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

jwtDecoder.setJwtValidator(withAudience);

return jwtDecoder;
}
提示

如前所述,你也可以选择在 Boot 中配置 aud 验证。

配置声明集映射​

Spring Security 使用 Nimbus 库来解析 JWT 并验证其签名。因此,Spring Security 遵循 Nimbus 对每个字段值的解释以及如何将其强制转换为 Java 类型。

例如,由于Nimbus保持对Java 7的兼容性,它不使用Instant来表示时间戳字段。

而且完全可以使用其他库进行JWT处理,这些库可能有自己的强制类型转换规则,需要相应调整。

或者,更简单地说,资源服务器可能出于特定领域的原因,想要在JWT中添加或移除声明。

为此,Resource Server 支持使用 MappedJwtClaimSetConverter 来映射 JWT 声明集。

自定义单个声明的转换​

默认情况下,MappedJwtClaimSetConverter 会尝试将声明转换为以下类型:

声明Java 类型
audCollection<String>
expInstant
iatInstant
issString
jtiString
nbfInstant
subString

单个声明的转换策略可以通过 MappedJwtClaimSetConverter.withDefaults 进行配置:

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();

MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
.withDefaults(Collections.singletonMap("sub", this::lookupUserIdBySub));
jwtDecoder.setClaimSetConverter(converter);

return jwtDecoder;
}

这将保留所有默认设置,但会覆盖 sub 的默认声明转换器。

添加声明​

MappedJwtClaimSetConverter 也可用于添加自定义声明,例如,以适应现有系统:

MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("custom", custom -> "value"));

移除声明​

移除声明也同样简单,使用相同的API即可:

MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("legacyclaim", legacy -> null));

重命名声明​

在更复杂的场景中,例如同时查询多个声明或重命名声明时,资源服务器接受任何实现了 Converter<Map<String, Object>, Map<String,Object>> 接口的类:

public class UsernameSubClaimAdapter implements Converter<Map<String, Object>, Map<String, Object>> {
private final MappedJwtClaimSetConverter delegate =
MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap());

public Map<String, Object> convert(Map<String, Object> claims) {
Map<String, Object> convertedClaims = this.delegate.convert(claims);

String username = (String) convertedClaims.get("user_name");
convertedClaims.put("sub", username);

return convertedClaims;
}
}

然后,可以像平常一样提供实例:

@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());
return jwtDecoder;
}

配置超时设置​

默认情况下,资源服务器在与授权服务器协调时,连接超时和套接字超时均设置为30秒。

在某些场景下,这可能过于简短。此外,它没有考虑到更复杂的模式,比如回退和发现。

要调整资源服务器连接授权服务器的方式,NimbusJwtDecoder 可接收一个 RestOperations 实例:

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

NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build();
return jwtDecoder;
}

默认情况下,资源服务器会在内存中缓存授权服务器的JWK集5分钟,您可能需要调整此设置。此外,它没有考虑更复杂的缓存模式,例如缓存逐出或使用共享缓存。

要调整资源服务器缓存JWK集的方式,NimbusJwtDecoder 接受一个 Cache 实例:

@Bean
public JwtDecoder jwtDecoder(CacheManager cacheManager) {
return NimbusJwtDecoder.withIssuerLocation(issuer)
.cache(cacheManager.getCache("jwks"))
.build();
}

当给定一个 Cache 时,资源服务器将使用 JWK Set URI 作为键,JWK Set JSON 作为值。

备注

Spring 本身并非缓存提供者,因此你需要确保包含适当的依赖项,例如 spring-boot-starter-cache 以及你偏好的缓存提供者。

备注

无论是套接字还是缓存超时,您可能更希望直接使用 Nimbus。为此,请记住 NimbusJwtDecoder 附带了一个接受 Nimbus 的 JWTProcessor 的构造函数。