OAuth 2.0 Resource Server JWT
Minimal Dependencies for JWT
大部分资源服务器支持收集到 spring-security-oauth2-resource-server
中。然而,解码和验证 JWT 的支持在 spring-security-oauth2-jose
中,这意味着两者对于拥有支持 JWT 编码承载令牌的可用资源服务器都是必需的。
Most Resource Server support is collected into spring-security-oauth2-resource-server
.
However, the support for decoding and verifying JWTs is in spring-security-oauth2-jose
, meaning that both are necessary in order to have a working resource server that supports JWT-encoded Bearer Tokens.
Minimal Configuration for JWTs
当使用 Spring Boot 时,将应用程序配置为资源服务器包括两个基本步骤。首先,包含所需的依赖项,其次,指示授权服务器的位置。
When using Spring Boot, configuring an application as a resource server consists of two basic steps. First, include the needed dependencies and second, indicate the location of the authorization server.
Specifying the Authorization Server
在Spring Boot应用中,若要指定要使用哪一个授权服务器,只需执行以下操作:
In a Spring Boot application, to specify which authorization server to use, simply do:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com/issuer
其中,`https://idp.example.com/issuer`是授权服务器要颁发的JWT令牌中包含的`iss`声明中的值。资源服务器会使用此属性进行进一步的自我配置、发现授权服务器的公钥并随后验证入站JWT。
Where https://idp.example.com/issuer
is the value contained in the iss
claim for JWT tokens that the authorization server will issue.
Resource Server will use this property to further self-configure, discover the authorization server’s public keys, and subsequently validate incoming JWTs.
若要使用 |
To use the |
这就是全部!
And that’s it!
Startup Expectations
当使用此属性和这些依赖项时,资源服务器将自动配置其自身来验证经过JWT编码的Bearer令牌。
When this property and these dependencies are used, Resource Server will automatically configure itself to validate JWT-encoded Bearer Tokens.
它通过确定性的启动过程实现这一点:
It achieves this through a deterministic startup process:
-
Query the Provider Configuration or Authorization Server Metadata endpoint for the
jwks_url
property -
Query the
jwks_url
endpoint for supported algorithms -
Configure the validation strategy to query
jwks_url
for valid public keys of the algorithms found -
Configure the validation strategy to validate each JWTs
iss
claim againsthttps://idp.example.com
.
这个过程的后果是授权服务器必须处于启动状态并接收请求,以便资源服务器成功启动。
A consequence of this process is that the authorization server must be up and receiving requests in order for Resource Server to successfully start up.
如果资源服务器在查询它(给定适当的超时)时,授权服务器关闭,然后启动将失败。 |
If the authorization server is down when Resource Server queries it (given appropriate timeouts), then startup will fail. |
Runtime Expectations
应用程序启动后,资源服务器将尝试处理包含`Authorization: Bearer`标头的任何请求:
Once the application is started up, Resource Server will attempt to process any request containing an Authorization: Bearer
header:
GET / HTTP/1.1
Authorization: Bearer some-token-value # Resource Server will process this
只要指出了此方案,资源服务器会尝试根据Bearer令牌规范处理请求。
So long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.
鉴于经过格式良好的JWT,资源服务器将:
Given a well-formed JWT, Resource Server will:
-
Validate its signature against a public key obtained from the
jwks_url
endpoint during startup and matched against the JWT -
Validate the JWT’s
exp
andnbf
timestamps and the JWT’siss
claim, and -
Map each scope to an authority with the prefix
SCOPE_
.
当授权服务器提供新密钥时,Spring Security 将自动轮换用于验证 JWT 的密钥。 |
As the authorization server makes available new keys, Spring Security will automatically rotate the keys used to validate JWTs. |
产生的`Authentication#getPrincipal`在默认情况下是Spring Security`Jwt`对象,如果存在,`Authentication#getName`映射到JWT的`sub`属性。
The resulting Authentication#getPrincipal
, by default, is a Spring Security Jwt
object, and Authentication#getName
maps to the JWT’s sub
property, if one is present.
从此处,考虑跳转到:
From here, consider jumping to:
How JWT Authentication Works
接下来,让我们看看 Spring Security 用于支持类似于我们刚才看到的基于 servlet 的应用程序中的 JWT 身份验证的体系结构组件。
Next, let’s see the architectural components that Spring Security uses to support JWT Authentication in servlet-based applications, like the one we just saw.
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[JwtAuthenticationProvider
] 是一种 AuthenticationProvider
实现,它利用 <<`JwtDecoder`,oauth2resourceserver-jwt-decoder>> 和 <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-authorization-extraction>> 对 JWT 进行身份验证。
{security-api-url}org/springframework/security/oauth2/server/resource/authentication/JwtAuthenticationProvider.html[JwtAuthenticationProvider
] is an AuthenticationProvider
implementation that leverages a <<`JwtDecoder`,oauth2resourceserver-jwt-decoder>> and <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-authorization-extraction>> to authenticate a JWT.
让我们来看看 JwtAuthenticationProvider
如何在 Spring Security 中工作。此图说明了 Reading the Bearer Token 图中的 AuthenticationManager
如何工作的详细信息。
Let’s take a look at how JwtAuthenticationProvider
works within Spring Security.
The figure explains details of how the AuthenticationManager
in figures from oauth2resourceserver-authentication-bearertokenauthenticationfilter works.
JwtAuthenticationProvider
Usage 来自 Reading the Bearer Token 的身份验证 Filter
向 AuthenticationManager
传递了 BearerTokenAuthenticationToken
,而 ProviderManager
实现了 AuthenticationManager
。
The authentication Filter
from oauth2resourceserver-authentication-bearertokenauthenticationfilter passes a BearerTokenAuthenticationToken
to the AuthenticationManager
which is implemented by ProviderManager
.
已配置为使用类型为 JwtAuthenticationProvider
的 AuthenticationProvider。
The ProviderManager
is configured to use an AuthenticationProvider of type JwtAuthenticationProvider
.
JwtAuthenticationProvider
使用 <<`JwtDecoder`,oauth2resourceserver-jwt-decoder>> 解码、验证和验证 Jwt
。
JwtAuthenticationProvider
decodes, verifies, and validates the Jwt
using a <<`JwtDecoder`,oauth2resourceserver-jwt-decoder>>.
JwtAuthenticationProvider
然后使用 <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-authorization-extraction>> 将 Jwt
转换为被授予权限的 Collection
。
JwtAuthenticationProvider
then uses the <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-authorization-extraction>> to convert the Jwt
into a Collection
of granted authorities.
当身份验证成功时,返回的 Authentication
是 JwtAuthenticationToken
类型,并且有一个主体,即由已配置的 JwtDecoder
返回的 Jwt
。最终,返回的 JwtAuthenticationToken
将由身份验证 Filter
设置在 SecurityContextHolder
上。
When authentication is successful, the Authentication
that is returned is of type JwtAuthenticationToken
and has a principal that is the Jwt
returned by the configured JwtDecoder
.
Ultimately, the returned JwtAuthenticationToken
will be set on the SecurityContextHolder
by the authentication Filter
.
Specifying the Authorization Server JWK Set Uri Directly
如果授权服务器不支持任何配置端点,或者如果资源服务器必须能够独立于授权服务器启动,那么可以提供`jwk-set-uri`:
If the authorization server doesn’t support any configuration endpoints, or if Resource Server must be able to start up independently from the authorization server, then the jwk-set-uri
can be supplied as well:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com
jwk-set-uri: https://idp.example.com/.well-known/jwks.json
JWK 集 uri 没有标准化,但通常可以在授权服务器的文档中找到它 |
The JWK Set uri is not standardized, but can typically be found in the authorization server’s documentation |
因此,Resource Server 没有在启动时检查授权服务器。我们仍然指定 issuer-uri
,以便 Resource Server 仍然验证传入 JWT 的 iss
声明。
Consequently, Resource Server will not ping the authorization server at startup.
We still specify the issuer-uri
so that Resource Server still validates the iss
claim on incoming JWTs.
此属性也可以直接在 DSL 上提供。 |
This property can also be supplied directly on the oauth2resourceserver-jwt-jwkseturi-dsl. |
Supplying Audiences
如已经看到,<<`issuer-uri` 属性验证 iss
声明,_指定_授权服务器>>; 这是发送 JWT 的对象。
As already seen, the <<`issuer-uri` property validates the iss
claim,_specifying_the_authorization_server>>; this is who sent the JWT.
Boot 也具有 audiences
属性用于验证 aud
声明;这是 JWT 的发送对象。
Boot also has the audiences
property for validating the aud
claim; this is who the JWT was sent to.
可以如下所示指示资源服务器的受众:
A resource server’s audience can be indicated like so:
spring:
security:
oauth2:
resourceserver:
jwt:
issuer-uri: https://idp.example.com
audiences: https://my-resource-server.example.com
如果需要,你也可以添加 the |
You can also add oauth2resourceserver-jwt-validation-custom, if needed. |
结果为,如果 JWT 的 iss
声明不是 https://idp.example.com
,并且它的 aud
声明在列表中不包含 https://my-resource-server.example.com
,则验证将失败。
The result will be that if the JWT’s iss
claim is not https://idp.example.com
, and its aud
claim does not contain https://my-resource-server.example.com
in its list, then validation will fail.
Overriding or Replacing Boot Auto Configuration
有 Spring Boot 从 Resource Server 创建的两个 @Bean
。
There are two `@Bean`s that Spring Boot generates on Resource Server’s behalf.
第一个是将应用程序配置为资源服务器的 SecurityFilterChain
。在包含 spring-security-oauth2-jose
时,此 SecurityFilterChain
如下所示:
The first is a SecurityFilterChain
that configures the app as a resource server. When including spring-security-oauth2-jose
, this SecurityFilterChain
looks like:
-
Java
-
Kotlin
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(authorize -> authorize
.anyRequest().authenticated()
)
.oauth2ResourceServer((oauth2) -> oauth2.jwt(Customizer.withDefaults()));
return http.build();
}
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt { }
}
}
return http.build()
}
如果应用程序不公开 SecurityFilterChain
bean,则 Spring Boot 会公开上述默认值。
If the application doesn’t expose a SecurityFilterChain
bean, then Spring Boot will expose the above default one.
替换它就像在应用程序中公开 bean:
Replacing this is as simple as exposing the bean within the application:
-
Java
-
Kotlin
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();
}
}
import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope
@Configuration
@EnableWebSecurity
class MyCustomSecurityConfiguration {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/messages/**", hasScope("message:read"))
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt {
jwtAuthenticationConverter = myConverter()
}
}
}
return http.build()
}
}
上面要求任何从 /messages/
开头的 URL 拥有 message:read
作用域。
The above requires the scope of message:read
for any URL that starts with /messages/
.
oauth2ResourceServer
DSL 上的方法也会覆盖或替换自动配置。
Methods on the oauth2ResourceServer
DSL will also override or replace auto configuration.
例如,Spring Boot 创建的第二个 @Bean
是 JwtDecoder
,它 decodes String
tokens into validated instances of Jwt
:
For example, the second @Bean
Spring Boot creates is a JwtDecoder
, which oauth2resourceserver-jwt-architecture-jwtdecoder:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder() {
return JwtDecoders.fromIssuerLocation(issuerUri);
}
@Bean
fun jwtDecoder(): JwtDecoder {
return JwtDecoders.fromIssuerLocation(issuerUri)
}
调用 |
Calling |
如果应用程序不公开 JwtDecoder
bean,则 Spring Boot 会公开上述默认值。
If the application doesn’t expose a JwtDecoder
bean, then Spring Boot will expose the above default one.
并且可以使用 jwkSetUri()
替换配置或使用 decoder()
。
And its configuration can be overridden using jwkSetUri()
or replaced using decoder()
.
或者,如果您根本不使用 Spring Boot,则可以在 XML 中指定这两个组件 - 过滤器链和 JwtDecoder
。
Or, if you’re not using Spring Boot at all, then both of these components - the filter chain and a JwtDecoder
can be specified in XML.
过滤器链是这样指定的:
The filter chain is specified like so:
-
Xml
<http>
<intercept-uri pattern="/**" access="authenticated"/>
<oauth2-resource-server>
<jwt decoder-ref="jwtDecoder"/>
</oauth2-resource-server>
</http>
并且 JwtDecoder
如下所示:
And the JwtDecoder
like so:
-
Xml
<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>
Using jwkSetUri()
授权服务器的 JWK 设置 URI 可以在 as a configuration property 配置,或者它可以在 DSL 中提供:
An authorization server’s JWK Set Uri can be configured oauth2resourceserver-jwt-jwkseturi or it can be supplied in the DSL:
-
Java
-
Kotlin
-
Xml
@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();
}
}
@Configuration
@EnableWebSecurity
class DirectlyConfiguredJwkSetUri {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt {
jwkSetUri = "https://idp.example.com/.well-known/jwks.json"
}
}
}
return http.build()
}
}
<http>
<intercept-uri pattern="/**" access="authenticated"/>
<oauth2-resource-server>
<jwt jwk-set-uri="https://idp.example.com/.well-known/jwks.json"/>
</oauth2-resource-server>
</http>
使用 jwkSetUri()
优先于任何配置属性。
Using jwkSetUri()
takes precedence over any configuration property.
Using decoder()
比 jwkSetUri()
更强大的是 decoder()
,它将完全替换 <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>> 的任何 Boot 自动配置:
More powerful than jwkSetUri()
is decoder()
, which will completely replace any Boot auto configuration of <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>>:
-
Java
-
Kotlin
-
Xml
@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();
}
}
@Configuration
@EnableWebSecurity
class DirectlyConfiguredJwtDecoder {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt {
jwtDecoder = myCustomDecoder()
}
}
}
return http.build()
}
}
<http>
<intercept-uri pattern="/**" access="authenticated"/>
<oauth2-resource-server>
<jwt decoder-ref="myCustomDecoder"/>
</oauth2-resource-server>
</http>
当需要深入配置,诸如 validation、mapping 或 request timeouts 时,这非常方便。
This is handy when deeper configuration, like oauth2resourceserver-jwt-validation, oauth2resourceserver-jwt-claimsetmapping, or oauth2resourceserver-jwt-timeouts, is necessary.
Exposing a JwtDecoder
@Bean
或者,公开一个 <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>> @Bean
的效果与 decoder()
相同。您可以用 jwkSetUri
构造一个,如下所示:
Or, exposing a <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>> @Bean
has the same effect as decoder()
.
You can construct one with a jwkSetUri
like so:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build()
}
或者,您可以使用颁发者并让 NimbusJwtDecoder
在调用 build()
时查找 jwkSetUri
,如下所示:
or you can use the issuer and have NimbusJwtDecoder
look up the jwkSetUri
when build()
is invoked, like the following:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(issuer).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withIssuerLocation(issuer).build()
}
或者,如果默认设置对您有效,您还可以使用 JwtDecoders
,它除了配置解码器的验证器外还会执行上述操作:
Or, if the defaults work for you, you can also use JwtDecoders
, which does the above in addition to configuring the decoder’s validator:
-
Java
-
Kotlin
@Bean
public JwtDecoders jwtDecoder() {
return JwtDecoders.fromIssuerLocation(issuer);
}
@Bean
fun jwtDecoder(): JwtDecoders {
return JwtDecoders.fromIssuerLocation(issuer)
}
Configuring Trusted Algorithms
默认情况下,NimbusJwtDecoder
及由此及出的资源服务器只会使用 RS256
信任并验证令牌。
By default, NimbusJwtDecoder
, and hence Resource Server, will only trust and verify tokens using RS256
.
您可以通过 Spring Boot、the NimbusJwtDecoder builder 或 JWK Set response 来自定义此操作。
You can customize this via oauth2resourceserver-jwt-boot-algorithm, oauth2resourceserver-jwt-decoder-builder, or from the oauth2resourceserver-jwt-decoder-jwk-response.
Via Spring Boot
设置算法的最简单方法是用作属性:
The simplest way to set the algorithm is as a property:
spring:
security:
oauth2:
resourceserver:
jwt:
jws-algorithms: RS512
jwk-set-uri: https://idp.example.org/.well-known/jwks.json
Using a Builder
然而,为了获得更大的能力,我们可以使用 NimbusJwtDecoder
附带的生成器:
For greater power, though, we can use a builder that ships with NimbusJwtDecoder
:
-
Java
-
Kotlin
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).build()
}
多次调用 jwsAlgorithm
将配置 NimbusJwtDecoder
以信任一种以上的算法,如下所示:
Calling jwsAlgorithm
more than once will configure NimbusJwtDecoder
to trust more than one algorithm, like so:
-
Java
-
Kotlin
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithm(RS512).jwsAlgorithm(ES512).build()
}
或者,您可以调用 jwsAlgorithms
:
Or, you can call jwsAlgorithms
:
-
Java
-
Kotlin
@Bean
JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithms(algorithms -> {
algorithms.add(RS512);
algorithms.add(ES512);
}).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withIssuerLocation(this.issuer)
.jwsAlgorithms {
it.add(RS512)
it.add(ES512)
}.build()
}
From JWK Set response
由于 Spring Security 的 JWT 支持基于 Nimbus,因此您也可以使用它所有出色的功能。
Since Spring Security’s JWT support is based off of Nimbus, you can use all it’s great features as well.
例如,Nimbus 有一个 JWSKeySelector
实现,它将根据 JWK 设置 URI 响应选择一组算法。您可以用它生成 NimbusJwtDecoder
,如下所示:
For example, Nimbus has a JWSKeySelector
implementation that will select the set of algorithms based on the JWK Set URI response.
You can use it to generate a NimbusJwtDecoder
like so:
-
Java
-
Kotlin
@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);
}
@Bean
fun jwtDecoder(): JwtDecoder {
// makes a request to the JWK Set endpoint
val jwsKeySelector: JWSKeySelector<SecurityContext> = JWSAlgorithmFamilyJWSKeySelector.fromJWKSetURL<SecurityContext>(this.jwkSetUrl)
val jwtProcessor: DefaultJWTProcessor<SecurityContext> = DefaultJWTProcessor()
jwtProcessor.jwsKeySelector = jwsKeySelector
return NimbusJwtDecoder(jwtProcessor)
}
Trusting a Single Asymmetric Key
比使用 JWK 设置端点支持资源服务器的更简单的做法是硬编码 RSA 公钥。可以通过 Spring Boot 或通过 Using a Builder 提供公钥。
Simpler than backing a Resource Server with a JWK Set endpoint is to hard-code an RSA public key. The public key can be provided via oauth2resourceserver-jwt-decoder-public-key-boot or by oauth2resourceserver-jwt-decoder-public-key-builder.
Via Spring Boot
通过 Spring Boot 指定密钥非常简单。密钥的位置可以如下指定:
Specifying a key via Spring Boot is quite simple. The key’s location can be specified like so:
spring:
security:
oauth2:
resourceserver:
jwt:
public-key-location: classpath:my-key.pub
或者,为了允许更复杂的查找,您可以对 RsaKeyConversionServicePostProcessor
进行后处理:
Or, to allow for a more sophisticated lookup, you can post-process the RsaKeyConversionServicePostProcessor
:
-
Java
-
Kotlin
@Bean
BeanFactoryPostProcessor conversionServiceCustomizer() {
return beanFactory ->
beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)
.setResourceLoader(new CustomResourceLoader());
}
@Bean
fun conversionServiceCustomizer(): BeanFactoryPostProcessor {
return BeanFactoryPostProcessor { beanFactory ->
beanFactory.getBean<RsaKeyConversionServicePostProcessor>()
.setResourceLoader(CustomResourceLoader())
}
}
指定您的密钥位置:
Specify your key’s location:
key.location: hfds://my-key.pub
然后自动装配值:
And then autowire the value:
-
Java
-
Kotlin
@Value("${key.location}")
RSAPublicKey key;
@Value("\${key.location}")
val key: RSAPublicKey? = null
Using a Builder
要直接接线 RSAPublicKey
,您只需使用适当的 NimbusJwtDecoder
生成器,如下所示:
To wire an RSAPublicKey
directly, you can simply use the appropriate NimbusJwtDecoder
builder, like so:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withPublicKey(this.key).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withPublicKey(this.key).build()
}
Trusting a Single Symmetric Key
使用单一对称密钥也很简单。您只需加载 SecretKey
并使用适当的 NimbusJwtDecoder
生成器,如下所示:
Using a single symmetric key is also simple.
You can simply load in your SecretKey
and use the appropriate NimbusJwtDecoder
builder, like so:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder() {
return NimbusJwtDecoder.withSecretKey(this.key).build();
}
@Bean
fun jwtDecoder(): JwtDecoder {
return NimbusJwtDecoder.withSecretKey(key).build()
}
Configuring Authorization
OAuth 2.0 授权服务器颁发的 JWT 通常具有 scope
或 scp
特性,表示已授予的范围(或权限),例如:
A JWT that is issued from an OAuth 2.0 Authorization Server will typically either have a scope
or scp
attribute, indicating the scopes (or authorities) it’s been granted, for example:
{ …, "scope" : "messages contacts"}
如果是这种情况,资源服务器将尝试强制将这些作用域转换为已授予权限列表,用字符串“SCOPE_”作为每个作用域的前缀。
When this is the case, Resource Server will attempt to coerce these scopes into a list of granted authorities, prefixing each scope with the string "SCOPE_".
这意味着,要使用源自 JWT 的作用域保护端点或方法,相应的表达式应包括此前缀:
This means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:
-
Java
-
Kotlin
-
Xml
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(OAuth2ResourceServerConfigurer::jwt);
return http.build();
}
}
import org.springframework.security.oauth2.core.authorization.OAuth2AuthorizationManagers.hasScope;
@Configuration
@EnableWebSecurity
class DirectlyConfiguredJwkSetUri {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize("/contacts/**", hasScope("contacts"))
authorize("/messages/**", hasScope("messages"))
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt { }
}
}
return http.build()
}
}
<http>
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
<oauth2-resource-server>
<jwt jwk-set-uri="https://idp.example.org/.well-known/jwks.json"/>
</oauth2-resource-server>
</http>
或者与此类似的方法安全性:
Or similarly with method security:
-
Java
-
Kotlin
@PreAuthorize("hasAuthority('SCOPE_messages')")
public List<Message> getMessages(...) {}
@PreAuthorize("hasAuthority('SCOPE_messages')")
fun getMessages(): List<Message> { }
Extracting Authorities Manually
但是,在多种情况下,此默认值都是不够的。例如,一些授权服务器未使用 scope
属性,而是有自己的自定义属性。或者,在其他时候,资源服务器可能需要根据内部化权限调整属性或一组属性。
However, there are a number of circumstances where this default is insufficient.
For example, some authorization servers don’t use the scope
attribute, but instead have their own custom attribute.
Or, at other times, the resource server may need to adapt the attribute or a composition of attributes into internalized authorities.
为此,Spring Security 附带了 JwtAuthenticationConverter
,它负责 converting a Jwt
into an Authentication
。默认情况下,Spring Security 会用 JwtAuthenticationConverter
的默认实例连接 JwtAuthenticationProvider
。
To this end, Spring Security ships with JwtAuthenticationConverter
, which is responsible for oauth2resourceserver-jwt-architecture-jwtauthenticationconverter.
By default, Spring Security will wire the JwtAuthenticationProvider
with a default instance of JwtAuthenticationConverter
.
作为配置 JwtAuthenticationConverter
的一部分,您可以提供一个从 Jwt
到 Collection
的已授予权限的子转换器。
As part of configuring a JwtAuthenticationConverter
, you can supply a subsidiary converter to go from Jwt
to a Collection
of granted authorities.
假设您的授权服务器在名为 authorities
的自定义声明中传达权限。在这种情况下,您可以配置 <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-architecture-jwtauthenticationconverter>> 应检查的声明,如下所示:
Let’s say that that your authorization server communicates authorities in a custom claim called authorities
.
In that case, you can configure the claim that <<`JwtAuthenticationConverter`,oauth2resourceserver-jwt-architecture-jwtauthenticationconverter>> should inspect, like so:
-
Java
-
Kotlin
-
Xml
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
@Bean
fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
val grantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()
grantedAuthoritiesConverter.setAuthoritiesClaimName("authorities")
val jwtAuthenticationConverter = JwtAuthenticationConverter()
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter)
return jwtAuthenticationConverter
}
<http>
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
<oauth2-resource-server>
<jwt jwk-set-uri="https://idp.example.org/.well-known/jwks.json"
jwt-authentication-converter-ref="jwtAuthenticationConverter"/>
</oauth2-resource-server>
</http>
<bean id="jwtAuthenticationConverter"
class="org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter">
<property name="jwtGrantedAuthoritiesConverter" ref="jwtGrantedAuthoritiesConverter"/>
</bean>
<bean id="jwtGrantedAuthoritiesConverter"
class="org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter">
<property name="authoritiesClaimName" value="authorities"/>
</bean>
您还可以将权限前缀配置为有所不同。您可以将前缀从 SCOPE_
更改为 ROLE_
,如下所示,而不仅仅是用 SCOPE_
作为每个权限的前缀:
You can also configure the authority prefix to be different as well.
Instead of prefixing each authority with SCOPE_
, you can change it to ROLE_
like so:
-
Java
-
Kotlin
-
Xml
@Bean
public JwtAuthenticationConverter jwtAuthenticationConverter() {
JwtGrantedAuthoritiesConverter grantedAuthoritiesConverter = new JwtGrantedAuthoritiesConverter();
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_");
JwtAuthenticationConverter jwtAuthenticationConverter = new JwtAuthenticationConverter();
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter);
return jwtAuthenticationConverter;
}
@Bean
fun jwtAuthenticationConverter(): JwtAuthenticationConverter {
val grantedAuthoritiesConverter = JwtGrantedAuthoritiesConverter()
grantedAuthoritiesConverter.setAuthorityPrefix("ROLE_")
val jwtAuthenticationConverter = JwtAuthenticationConverter()
jwtAuthenticationConverter.setJwtGrantedAuthoritiesConverter(grantedAuthoritiesConverter)
return jwtAuthenticationConverter
}
<http>
<intercept-uri pattern="/contacts/**" access="hasAuthority('SCOPE_contacts')"/>
<intercept-uri pattern="/messages/**" access="hasAuthority('SCOPE_messages')"/>
<oauth2-resource-server>
<jwt jwk-set-uri="https://idp.example.org/.well-known/jwks.json"
jwt-authentication-converter-ref="jwtAuthenticationConverter"/>
</oauth2-resource-server>
</http>
<bean id="jwtAuthenticationConverter"
class="org.springframework.security.oauth2.server.resource.authentication.JwtAuthenticationConverter">
<property name="jwtGrantedAuthoritiesConverter" ref="jwtGrantedAuthoritiesConverter"/>
</bean>
<bean id="jwtGrantedAuthoritiesConverter"
class="org.springframework.security.oauth2.server.resource.authentication.JwtGrantedAuthoritiesConverter">
<property name="authorityPrefix" value="ROLE_"/>
</bean>
或者,您可以通过调用 JwtGrantedAuthoritiesConverter#setAuthorityPrefix("")
完全移除前缀。
Or, you can remove the prefix altogether by calling JwtGrantedAuthoritiesConverter#setAuthorityPrefix("")
.
为了更灵活,DSL 支持完全用任何实现 Converter<Jwt, AbstractAuthenticationToken>
的类替换转换器:
For more flexibility, the DSL supports entirely replacing the converter with any class that implements Converter<Jwt, AbstractAuthenticationToken>
:
-
Java
-
Kotlin
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();
}
}
internal class CustomAuthenticationConverter : Converter<Jwt, AbstractAuthenticationToken> {
override fun convert(jwt: Jwt): AbstractAuthenticationToken {
return CustomAuthenticationToken(jwt)
}
}
// ...
@Configuration
@EnableWebSecurity
class CustomAuthenticationConverterConfig {
@Bean
open fun filterChain(http: HttpSecurity): SecurityFilterChain {
http {
authorizeRequests {
authorize(anyRequest, authenticated)
}
oauth2ResourceServer {
jwt {
jwtAuthenticationConverter = CustomAuthenticationConverter()
}
}
}
return http.build()
}
}
Configuring Validation
通过使用 minimal Spring Boot configuration 指出授权服务器的发行人 URI,资源服务器将默认验证 iss
声明以及 exp
和 nbf
时间戳声明。
Using oauth2resourceserver-jwt-minimalconfiguration, indicating the authorization server’s issuer uri, Resource Server will default to verifying the iss
claim as well as the exp
and nbf
timestamp claims.
在需要对验证进行自定义的情况下,资源服务器附带两个标准验证器,并且还接受自定义 OAuth2TokenValidator
实例。
In circumstances where validation needs to be customized, Resource Server ships with two standard validators and also accepts custom OAuth2TokenValidator
instances.
Customizing Timestamp Validation
JWT 通常有一个有效期窗口,窗口的开始用 nbf
声明表示,窗口的结束用 exp
声明表示。
JWT’s typically have a window of validity, with the start of the window indicated in the nbf
claim and the end indicated in the exp
claim.
但是,每个服务器都可能遇到时钟漂移,这可能导致某个服务器认为令牌已过期,而另一个服务器却没有。这可能会导致一些实现感到心烦,随着协作服务器的数量在分布式系统中增加,可能会出现这种情况。
However, every server can experience clock drift, which can cause tokens to appear expired to one server, but not to another. This can cause some implementation heartburn as the number of collaborating servers increases in a distributed system.
资源服务器使用 JwtTimestampValidator
验证令牌的有效期窗口,并且可以为其配置 clockSkew
以缓解上述问题:
Resource Server uses JwtTimestampValidator
to verify a token’s validity window, and it can be configured with a clockSkew
to alleviate the above problem:
-
Java
-
Kotlin
@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;
}
@Bean
fun jwtDecoder(): JwtDecoder {
val jwtDecoder: NimbusJwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) as NimbusJwtDecoder
val withClockSkew: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(
JwtTimestampValidator(Duration.ofSeconds(60)),
JwtIssuerValidator(issuerUri))
jwtDecoder.setJwtValidator(withClockSkew)
return jwtDecoder
}
默认情况下,资源服务器配置为时钟偏移 60 秒。 |
By default, Resource Server configures a clock skew of 60 seconds. |
Configuring a Custom Validator
使用 OAuth2TokenValidator
API,只需添加一个对 the aud
claim 的检查非常简单:
Adding a check for _supplying_audiences is simple with the OAuth2TokenValidator
API:
-
Java
-
Kotlin
OAuth2TokenValidator<Jwt> audienceValidator() {
return new JwtClaimValidator<List<String>>(AUD, aud -> aud.contains("messaging"));
}
fun audienceValidator(): OAuth2TokenValidator<Jwt?> {
return JwtClaimValidator<List<String>>(AUD) { aud -> aud.contains("messaging") }
}
或者,为了获得更多控制权,您可以实现自己的 OAuth2TokenValidator
:
Or, for more control you can implement your own OAuth2TokenValidator
:
-
Java
-
Kotlin
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();
}
internal class AudienceValidator : OAuth2TokenValidator<Jwt> {
var error: OAuth2Error = OAuth2Error("custom_code", "Custom error message", null)
override fun validate(jwt: Jwt): OAuth2TokenValidatorResult {
return if (jwt.audience.contains("messaging")) {
OAuth2TokenValidatorResult.success()
} else {
OAuth2TokenValidatorResult.failure(error)
}
}
}
// ...
fun audienceValidator(): OAuth2TokenValidator<Jwt> {
return AudienceValidator()
}
然后,要添加到资源服务器中,只需要指定 <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>> 实例即可:
Then, to add into a resource server, it’s a matter of specifying the <<`JwtDecoder`,oauth2resourceserver-jwt-architecture-jwtdecoder>> instance:
-
Java
-
Kotlin
@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;
}
@Bean
fun jwtDecoder(): JwtDecoder {
val jwtDecoder: NimbusJwtDecoder = JwtDecoders.fromIssuerLocation(issuerUri) as NimbusJwtDecoder
val audienceValidator = audienceValidator()
val withIssuer: OAuth2TokenValidator<Jwt> = JwtValidators.createDefaultWithIssuer(issuerUri)
val withAudience: OAuth2TokenValidator<Jwt> = DelegatingOAuth2TokenValidator(withIssuer, audienceValidator)
jwtDecoder.setJwtValidator(withAudience)
return jwtDecoder
}
如前所述,你可以改为 configure |
As stated earlier, you can instead _supplying_audiences. |
Configuring Claim Set Mapping
Spring Security 使用 Nimbus 库来解析 JWT 并验证其签名。因此,Spring Security 受 Nimbus 对每个字段值的解释以及如何将其强制转换为 Java 类型的限制。
Spring Security uses the Nimbus library for parsing JWTs and validating their signatures. Consequently, Spring Security is subject to Nimbus’s interpretation of each field value and how to coerce each into a Java type.
例如,因为 Nimbus 仍然兼容 Java 7,所以它不使用 Instant
来表示时间戳字段。
For example, because Nimbus remains Java 7 compatible, it doesn’t use Instant
to represent timestamp fields.
而且完全有可能使用不同的库或进行 JWT 处理,这可能会做出需要调整的自己的强制转换决策。
And it’s entirely possible to use a different library or for JWT processing, which may make its own coercion decisions that need adjustment.
或者,从根本上来说,资源服务器可能希望基于特定于域的原因向 JWT 添加或从中移除声明。
Or, quite simply, a resource server may want to add or remove claims from a JWT for domain-specific reasons.
出于这些目的,资源服务器支持使用 MappedJwtClaimSetConverter
映射 JWT 声明集。
For these purposes, Resource Server supports mapping the JWT claim set with MappedJwtClaimSetConverter
.
Customizing the Conversion of a Single Claim
默认情况下,MappedJwtClaimSetConverter
将尝试将声明强制转换为以下类型:
By default, MappedJwtClaimSetConverter
will attempt to coerce claims into the following types:
Claim |
Java Type |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
可以使用 MappedJwtClaimSetConverter.withDefaults
配置单个声明的转换策略:
An individual claim’s conversion strategy can be configured using MappedJwtClaimSetConverter.withDefaults
:
-
Java
-
Kotlin
@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
MappedJwtClaimSetConverter converter = MappedJwtClaimSetConverter
.withDefaults(Collections.singletonMap("sub", this::lookupUserIdBySub));
jwtDecoder.setClaimSetConverter(converter);
return jwtDecoder;
}
@Bean
fun jwtDecoder(): JwtDecoder {
val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()
val converter = MappedJwtClaimSetConverter
.withDefaults(mapOf("sub" to this::lookupUserIdBySub))
jwtDecoder.setClaimSetConverter(converter)
return jwtDecoder
}
这将保留所有默认值,但它将覆盖 sub
的默认声明转换器。
This will keep all the defaults, except it will override the default claim converter for sub
.
Adding a Claim
MappedJwtClaimSetConverter
也可以用于添加自定义声明,例如,以适应现有系统:
MappedJwtClaimSetConverter
can also be used to add a custom claim, for example, to adapt to an existing system:
-
Java
-
Kotlin
MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("custom", custom -> "value"));
MappedJwtClaimSetConverter.withDefaults(mapOf("custom" to Converter<Any, String> { "value" }))
Removing a Claim
并且移除声明也很简单,使用同样的 API:
And removing a claim is also simple, using the same API:
-
Java
-
Kotlin
MappedJwtClaimSetConverter.withDefaults(Collections.singletonMap("legacyclaim", legacy -> null));
MappedJwtClaimSetConverter.withDefaults(mapOf("legacyclaim" to Converter<Any, Any> { null }))
Renaming a Claim
在更加复杂的情景中,比如同时查阅多个声明或重命名一个声明,资源服务器接受实现 Converter<Map<String, Object>, Map<String,Object>>
的任何类:
In more sophisticated scenarios, like consulting multiple claims at once or renaming a claim, Resource Server accepts any class that implements Converter<Map<String, Object>, Map<String,Object>>
:
-
Java
-
Kotlin
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;
}
}
class UsernameSubClaimAdapter : Converter<Map<String, Any?>, Map<String, Any?>> {
private val delegate = MappedJwtClaimSetConverter.withDefaults(Collections.emptyMap())
override fun convert(claims: Map<String, Any?>): Map<String, Any?> {
val convertedClaims = delegate.convert(claims)
val username = convertedClaims["user_name"] as String
convertedClaims["sub"] = username
return convertedClaims
}
}
然后,可以像往常一样提供实例:
And then, the instance can be supplied like normal:
-
Java
-
Kotlin
@Bean
JwtDecoder jwtDecoder() {
NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build();
jwtDecoder.setClaimSetConverter(new UsernameSubClaimAdapter());
return jwtDecoder;
}
@Bean
fun jwtDecoder(): JwtDecoder {
val jwtDecoder: NimbusJwtDecoder = NimbusJwtDecoder.withIssuerLocation(issuer).build()
jwtDecoder.setClaimSetConverter(UsernameSubClaimAdapter())
return jwtDecoder
}
Configuring Timeouts
默认情况下,资源服务器针对协调授权服务器分别使用 30 秒的连接和套接字超时。
By default, Resource Server uses connection and socket timeouts of 30 seconds each for coordinating with the authorization server.
在某些情况下这可能太短。此外,它没有考虑到更复杂的模式,如回退和发现。
This may be too short in some scenarios. Further, it doesn’t take into account more sophisticated patterns like back-off and discovery.
要调整资源服务器连接到授权服务器的方式,NimbusJwtDecoder
接受一个 RestOperations
实例:
To adjust the way in which Resource Server connects to the authorization server, NimbusJwtDecoder
accepts an instance of RestOperations
:
-
Java
-
Kotlin
@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;
}
@Bean
fun jwtDecoder(builder: RestTemplateBuilder): JwtDecoder {
val rest: RestOperations = builder
.setConnectTimeout(Duration.ofSeconds(60))
.setReadTimeout(Duration.ofSeconds(60))
.build()
return NimbusJwtDecoder.withIssuerLocation(issuer).restOperations(rest).build()
}
此外,资源服务器默认将授权服务器的 JWK 集缓存在内存中 5 分钟,这可能是你希望调整的。此外,它没有考虑到更复杂的缓存模式,比如逐出或使用共享缓存。
Also by default, Resource Server caches in-memory the authorization server’s JWK set for 5 minutes, which you may want to adjust. Further, it doesn’t take into account more sophisticated caching patterns like eviction or using a shared cache.
要调整资源服务器缓存 JWK 集的方式,NimbusJwtDecoder
接受一个 Cache
实例:
To adjust the way in which Resource Server caches the JWK set, NimbusJwtDecoder
accepts an instance of Cache
:
-
Java
-
Kotlin
@Bean
public JwtDecoder jwtDecoder(CacheManager cacheManager) {
return NimbusJwtDecoder.withIssuerLocation(issuer)
.cache(cacheManager.getCache("jwks"))
.build();
}
@Bean
fun jwtDecoder(cacheManager: CacheManager): JwtDecoder {
return NimbusJwtDecoder.withIssuerLocation(issuer)
.cache(cacheManager.getCache("jwks"))
.build()
}
提供 Cache
时,资源服务器将使用 JWK 集 Uri 作为键,使用 JWK 集 JSON 作为值。
When given a Cache
, Resource Server will use the JWK Set Uri as the key and the JWK Set JSON as the value.
Spring 不是缓存提供程序,所以你需要确保包含适当的依赖项,如 |
Spring isn’t a cache provider, so you’ll need to make sure to include the appropriate dependencies, like |
无论是套接字还是缓存超时,你可能反而想直接使用 Nimbus。为此,请记住 |
Whether it’s socket or cache timeouts, you may instead want to work with Nimbus directly.
To do so, remember that |