|
@@ -430,15 +430,17 @@ First, include the needed dependencies and second, indicate the location of the
|
|
|
|
|
|
==== Specifying the Authorization Server
|
|
==== Specifying the Authorization Server
|
|
|
|
|
|
-To specify which authorization server to use, simply do:
|
|
|
|
|
|
+In a Spring Boot application, to specify which authorization server to use, simply do:
|
|
|
|
|
|
-```yaml
|
|
|
|
-security:
|
|
|
|
- oauth2:
|
|
|
|
- resourceserver:
|
|
|
|
- jwt:
|
|
|
|
- issuer-uri: https://idp.example.com
|
|
|
|
-```
|
|
|
|
|
|
+[source,yml]
|
|
|
|
+----
|
|
|
|
+spring:
|
|
|
|
+ security:
|
|
|
|
+ oauth2:
|
|
|
|
+ resourceserver:
|
|
|
|
+ jwt:
|
|
|
|
+ issuer-uri: https://idp.example.com
|
|
|
|
+----
|
|
|
|
|
|
Where `https://idp.example.com` is the value contained in the `iss` claim for JWT tokens that the authorization server will issue.
|
|
Where `https://idp.example.com` 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.
|
|
Resource Server will use this property to further self-configure, discover the authorization server's public keys, and subsequently validate incoming JWTs.
|
|
@@ -468,14 +470,15 @@ If the authorization server is down when Resource Server queries it (given appro
|
|
|
|
|
|
Once the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header:
|
|
Once the application is started up, Resource Server will attempt to process any request containing an `Authorization: Bearer` header:
|
|
|
|
|
|
-```http
|
|
|
|
|
|
+[source,html]
|
|
|
|
+----
|
|
GET / HTTP/1.1
|
|
GET / HTTP/1.1
|
|
Authorization: Bearer some-token-value # Resource Server will process this
|
|
Authorization: Bearer some-token-value # Resource Server will process this
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
So long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.
|
|
So long as this scheme is indicated, Resource Server will attempt to process the request according to the Bearer Token specification.
|
|
|
|
|
|
-Given a well-formed JWT, Resource Server will
|
|
|
|
|
|
+Given a well-formed JWT, Resource Server will:
|
|
|
|
|
|
1. Validate its signature against a public key obtained from the `jwks_url` endpoint during startup and matched against the JWTs header
|
|
1. Validate its signature against a public key obtained from the `jwks_url` endpoint during startup and matched against the JWTs header
|
|
2. Validate the JWTs `exp` and `nbf` timestamps and the JWTs `iss` claim, and
|
|
2. Validate the JWTs `exp` and `nbf` timestamps and the JWTs `iss` claim, and
|
|
@@ -497,13 +500,15 @@ From here, consider jumping to:
|
|
|
|
|
|
If the authorization server doesn't support the Provider Configuration endpoint, or if Resource Server must be able to start up independently from the authorization server, then `issuer-uri` can be exchanged for `jwk-set-uri`:
|
|
If the authorization server doesn't support the Provider Configuration endpoint, or if Resource Server must be able to start up independently from the authorization server, then `issuer-uri` can be exchanged for `jwk-set-uri`:
|
|
|
|
|
|
-```yaml
|
|
|
|
-security:
|
|
|
|
- oauth2:
|
|
|
|
- resourceserver:
|
|
|
|
- jwt:
|
|
|
|
- jwk-set-uri: https://idp.example.com/.well-known/jwks.json
|
|
|
|
-```
|
|
|
|
|
|
+[source,yaml]
|
|
|
|
+----
|
|
|
|
+spring:
|
|
|
|
+ security:
|
|
|
|
+ oauth2:
|
|
|
|
+ resourceserver:
|
|
|
|
+ jwt:
|
|
|
|
+ jwk-set-uri: https://idp.example.com/.well-known/jwks.json
|
|
|
|
+----
|
|
|
|
|
|
[NOTE]
|
|
[NOTE]
|
|
The JWK Set uri is not standardized, but can typically be found in the authorization server's documentation
|
|
The JWK Set uri is not standardized, but can typically be found in the authorization server's documentation
|
|
@@ -521,7 +526,8 @@ There are two `@Bean` s that Spring Boot generates on Resource Server's behalf.
|
|
|
|
|
|
The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `WebSecurityConfigurerAdapter` looks like:
|
|
The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server. When including `spring-security-oauth2-jose`, this `WebSecurityConfigurerAdapter` looks like:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
http
|
|
http
|
|
.authorizeRequests()
|
|
.authorizeRequests()
|
|
@@ -529,13 +535,14 @@ protected void configure(HttpSecurity http) {
|
|
.and()
|
|
.and()
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt)
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
|
|
If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
|
|
|
|
|
|
Replacing this is as simple as exposing the bean within the application:
|
|
Replacing this is as simple as exposing the bean within the application:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|
public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -549,7 +556,7 @@ public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter
|
|
.jwtAuthenticationConverter(myConverter());
|
|
.jwtAuthenticationConverter(myConverter());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
|
|
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
|
|
|
|
|
|
@@ -557,12 +564,13 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
|
|
|
|
|
|
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:
|
|
For example, the second `@Bean` Spring Boot creates is a `JwtDecoder`, which decodes `String` tokens into validated instances of `Jwt`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public JwtDecoder jwtDecoder() {
|
|
public JwtDecoder jwtDecoder() {
|
|
return JwtDecoders.fromOidcIssuerLocation(issuerUri);
|
|
return JwtDecoders.fromOidcIssuerLocation(issuerUri);
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
If the application doesn't expose a `JwtDecoder` bean, then Spring Boot will expose the above default one.
|
|
If the application doesn't expose a `JwtDecoder` bean, then Spring Boot will expose the above default one.
|
|
|
|
|
|
@@ -573,7 +581,8 @@ And its configuration can be overridden using `jwkSetUri()` or replaced using `d
|
|
|
|
|
|
An authorization server's JWK Set Uri can be configured <<oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:
|
|
An authorization server's JWK Set Uri can be configured <<oauth2resourceserver-jwt-jwkseturi,as a configuration property>> or it can be supplied in the DSL:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -586,7 +595,7 @@ public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
.jwkSetUri("https://idp.example.com/.well-known/jwks.json");
|
|
.jwkSetUri("https://idp.example.com/.well-known/jwks.json");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Using `jwkSetUri()` takes precedence over any configuration property.
|
|
Using `jwkSetUri()` takes precedence over any configuration property.
|
|
|
|
|
|
@@ -595,7 +604,8 @@ Using `jwkSetUri()` takes precedence over any configuration property.
|
|
|
|
|
|
More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of `JwtDecoder`:
|
|
More powerful than `jwkSetUri()` is `decoder()`, which will completely replace any Boot auto configuration of `JwtDecoder`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -608,7 +618,7 @@ public class DirectlyConfiguredJwtDecoder extends WebSecurityConfigurerAdapter {
|
|
.decoder(myCustomDecoder());
|
|
.decoder(myCustomDecoder());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.
|
|
This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validation,validation>>, <<oauth2resourceserver-jwt-claimsetmapping,mapping>>, or <<oauth2resourceserver-jwt-timeouts,request timeouts>>, is necessary.
|
|
|
|
|
|
@@ -617,26 +627,28 @@ This is handy when deeper configuration, like <<oauth2resourceserver-jwt-validat
|
|
|
|
|
|
Or, exposing a `JwtDecoder` `@Bean` has the same effect as `decoder()`:
|
|
Or, exposing a `JwtDecoder` `@Bean` has the same effect as `decoder()`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public JwtDecoder jwtDecoder() {
|
|
public JwtDecoder jwtDecoder() {
|
|
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
|
return NimbusJwtDecoder.withJwkSetUri(jwkSetUri).build();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-jwt-decoder-algorithm]]
|
|
[[oauth2resourceserver-jwt-decoder-algorithm]]
|
|
=== Configuring Trusted Algorithms
|
|
=== Configuring Trusted Algorithms
|
|
|
|
|
|
By default, `NimbusJwtDecoder`, and hence Resource Server, will only trust and verify tokens using `RS256`.
|
|
By default, `NimbusJwtDecoder`, and hence Resource Server, will only trust and verify tokens using `RS256`.
|
|
|
|
|
|
-You can customize this via <<oauth2-resourceserver-jwt-boot-algorithm,Spring Boot>>, <<oauth2-resourceserver-jwt-decoder-builder,the NimbusJwtDecoder builder>>, or from the <<oauth2-resourceserver-jwt-decoder-jwk-response,JWK Set response>>.
|
|
|
|
|
|
+You can customize this via <<oauth2resourceserver-jwt-boot-algorithm,Spring Boot>>, <<oauth2resourceserver-jwt-decoder-builder,the NimbusJwtDecoder builder>>, or from the <<oauth2resourceserver-jwt-decoder-jwk-response,JWK Set response>>.
|
|
|
|
|
|
-[[oauth2-resourceserver-jwt-boot-algorithm]]
|
|
|
|
|
|
+[[oauth2resourceserver-jwt-boot-algorithm]]
|
|
==== Via Spring Boot
|
|
==== Via Spring Boot
|
|
|
|
|
|
The simplest way to set the algorithm is as a property:
|
|
The simplest way to set the algorithm is as a property:
|
|
|
|
|
|
-```yaml
|
|
|
|
|
|
+[source,yaml]
|
|
|
|
+----
|
|
spring:
|
|
spring:
|
|
security:
|
|
security:
|
|
oauth2:
|
|
oauth2:
|
|
@@ -644,34 +656,37 @@ spring:
|
|
jwt:
|
|
jwt:
|
|
jws-algorithm: RS512
|
|
jws-algorithm: RS512
|
|
jwk-set-uri: https://idp.example.org/.well-known/jwks.json
|
|
jwk-set-uri: https://idp.example.org/.well-known/jwks.json
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
-[[oauth2-resourceserver-jwt-decoder-builder]]
|
|
|
|
|
|
+[[oauth2resourceserver-jwt-decoder-builder]]
|
|
==== Using a Builder
|
|
==== Using a Builder
|
|
|
|
|
|
For greater power, though, we can use a builder that ships with `NimbusJwtDecoder`:
|
|
For greater power, though, we can use a builder that ships with `NimbusJwtDecoder`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
JwtDecoder jwtDecoder() {
|
|
JwtDecoder jwtDecoder() {
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
.jwsAlgorithm(RS512).build();
|
|
.jwsAlgorithm(RS512).build();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Calling `jwsAlgorithm` more than once will configure `NimbusJwtDecoder` to trust more than one algorithm, like so:
|
|
Calling `jwsAlgorithm` more than once will configure `NimbusJwtDecoder` to trust more than one algorithm, like so:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
JwtDecoder jwtDecoder() {
|
|
JwtDecoder jwtDecoder() {
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
.jwsAlgorithm(RS512).jwsAlgorithm(EC512).build();
|
|
.jwsAlgorithm(RS512).jwsAlgorithm(EC512).build();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Or, you can call `jwsAlgorithms`:
|
|
Or, you can call `jwsAlgorithms`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
JwtDecoder jwtDecoder() {
|
|
JwtDecoder jwtDecoder() {
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
return NimbusJwtDecoder.fromJwkSetUri(this.jwkSetUri)
|
|
@@ -680,9 +695,9 @@ JwtDecoder jwtDecoder() {
|
|
algorithms.add(EC512);
|
|
algorithms.add(EC512);
|
|
}).build();
|
|
}).build();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
-[[oauth2-resourceserver-jwt-decoder-jwk-response]]
|
|
|
|
|
|
+[[oauth2resourceserver-jwt-decoder-jwk-response]]
|
|
==== From JWK Set response
|
|
==== From JWK Set response
|
|
|
|
|
|
Since Spring Security's JWT support is based off of Nimbus, you can use all it's great features as well.
|
|
Since Spring Security's JWT support is based off of Nimbus, you can use all it's great features as well.
|
|
@@ -717,25 +732,27 @@ The public key can be provided via <<oauth2resourceserver-jwt-decoder-public-key
|
|
Specifying a key via Spring Boot is quite simple.
|
|
Specifying a key via Spring Boot is quite simple.
|
|
The key's location can be specified like so:
|
|
The key's location can be specified like so:
|
|
|
|
|
|
-```yaml
|
|
|
|
|
|
+[source,yaml]
|
|
|
|
+----
|
|
spring:
|
|
spring:
|
|
security:
|
|
security:
|
|
oauth2:
|
|
oauth2:
|
|
resourceserver:
|
|
resourceserver:
|
|
jwt:
|
|
jwt:
|
|
public-key-location: classpath:my-key.pub
|
|
public-key-location: classpath:my-key.pub
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Or, to allow for a more sophisticated lookup, you can post-process the `RsaKeyConversionServicePostProcessor`:
|
|
Or, to allow for a more sophisticated lookup, you can post-process the `RsaKeyConversionServicePostProcessor`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
BeanFactoryPostProcessor conversionServiceCustomizer() {
|
|
BeanFactoryPostProcessor conversionServiceCustomizer() {
|
|
return beanFactory ->
|
|
return beanFactory ->
|
|
beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)
|
|
beanFactory.getBean(RsaKeyConversionServicePostProcessor.class)
|
|
.setResourceLoader(new CustomResourceLoader());
|
|
.setResourceLoader(new CustomResourceLoader());
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Specify your key's location:
|
|
Specify your key's location:
|
|
|
|
|
|
@@ -768,12 +785,13 @@ public JwtDecoder jwtDecoder() {
|
|
Using a single symmetric key is also simple.
|
|
Using a single symmetric key is also simple.
|
|
You can simply load in your `SecretKey` and use the appropriate `NimbusJwtDecoder` builder, like so:
|
|
You can simply load in your `SecretKey` and use the appropriate `NimbusJwtDecoder` builder, like so:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public JwtDecoder jwtDecoder() {
|
|
public JwtDecoder jwtDecoder() {
|
|
return NimbusJwtDecoder.withSecretKey(this.key).build();
|
|
return NimbusJwtDecoder.withSecretKey(this.key).build();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-jwt-authorization]]
|
|
[[oauth2resourceserver-jwt-authorization]]
|
|
=== Configuring Authorization
|
|
=== Configuring Authorization
|
|
@@ -786,7 +804,8 @@ When this is the case, Resource Server will attempt to coerce these scopes into
|
|
|
|
|
|
This means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:
|
|
This means that to protect an endpoint or method with a scope derived from a JWT, the corresponding expressions should include this prefix:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -799,14 +818,15 @@ public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::jwt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Or similarly with method security:
|
|
Or similarly with method security:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@PreAuthorize("hasAuthority('SCOPE_messages')")
|
|
@PreAuthorize("hasAuthority('SCOPE_messages')")
|
|
public List<Message> getMessages(...) {}
|
|
public List<Message> getMessages(...) {}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-jwt-authorization-extraction]]
|
|
[[oauth2resourceserver-jwt-authorization-extraction]]
|
|
==== Extracting Authorities Manually
|
|
==== Extracting Authorities Manually
|
|
@@ -817,7 +837,8 @@ Or, at other times, the resource server may need to adapt the attribute or a com
|
|
|
|
|
|
To this end, the DSL exposes `jwtAuthenticationConverter()`:
|
|
To this end, the DSL exposes `jwtAuthenticationConverter()`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredJwkSetUri extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -838,14 +859,15 @@ Converter<Jwt, AbstractAuthenticationToken> grantedAuthoritiesExtractor() {
|
|
(new GrantedAuthoritiesExtractor());
|
|
(new GrantedAuthoritiesExtractor());
|
|
return jwtAuthenticationConveter;
|
|
return jwtAuthenticationConveter;
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
which is responsible for converting a `Jwt` into an `Authentication`.
|
|
which is responsible for converting a `Jwt` into an `Authentication`.
|
|
As part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.
|
|
As part of its configuration, we can supply a subsidiary converter to go from `Jwt` to a `Collection` of granted authorities.
|
|
|
|
|
|
That final converter might be something like `GrantedAuthoritiesExtractor` below:
|
|
That final converter might be something like `GrantedAuthoritiesExtractor` below:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
static class GrantedAuthoritiesExtractor
|
|
static class GrantedAuthoritiesExtractor
|
|
implements Converter<Jwt, Collection<GrantedAuthority>> {
|
|
implements Converter<Jwt, Collection<GrantedAuthority>> {
|
|
|
|
|
|
@@ -858,17 +880,18 @@ static class GrantedAuthoritiesExtractor
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
For more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, AbstractAuthenticationToken>`:
|
|
For more flexibility, the DSL supports entirely replacing the converter with any class that implements `Converter<Jwt, AbstractAuthenticationToken>`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
static class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
|
static class CustomAuthenticationConverter implements Converter<Jwt, AbstractAuthenticationToken> {
|
|
public AbstractAuthenticationToken convert(Jwt jwt) {
|
|
public AbstractAuthenticationToken convert(Jwt jwt) {
|
|
return new CustomAuthenticationToken(jwt);
|
|
return new CustomAuthenticationToken(jwt);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-jwt-validation]]
|
|
[[oauth2resourceserver-jwt-validation]]
|
|
=== Configuring Validation
|
|
=== Configuring Validation
|
|
@@ -887,7 +910,8 @@ This can cause some implementation heartburn as the number of collaborating serv
|
|
|
|
|
|
Resource Server uses `JwtTimestampValidator` to verify a token's validity window, and it can be configured with a `clockSkew` to alleviate the above problem:
|
|
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
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
JwtDecoder jwtDecoder() {
|
|
JwtDecoder jwtDecoder() {
|
|
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
|
|
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
|
|
@@ -901,7 +925,7 @@ JwtDecoder jwtDecoder() {
|
|
|
|
|
|
return jwtDecoder;
|
|
return jwtDecoder;
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[NOTE]
|
|
[NOTE]
|
|
By default, Resource Server configures a clock skew of 30 seconds.
|
|
By default, Resource Server configures a clock skew of 30 seconds.
|
|
@@ -911,7 +935,8 @@ By default, Resource Server configures a clock skew of 30 seconds.
|
|
|
|
|
|
Adding a check for the `aud` claim is simple with the `OAuth2TokenValidator` API:
|
|
Adding a check for the `aud` claim is simple with the `OAuth2TokenValidator` API:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
|
public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
|
OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
|
|
OAuth2Error error = new OAuth2Error("invalid_token", "The required audience is missing", null);
|
|
|
|
|
|
@@ -923,11 +948,12 @@ public class AudienceValidator implements OAuth2TokenValidator<Jwt> {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Then, to add into a resource server, it's a matter of specifying the `JwtDecoder` instance:
|
|
Then, to add into a resource server, it's a matter of specifying the `JwtDecoder` instance:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
JwtDecoder jwtDecoder() {
|
|
JwtDecoder jwtDecoder() {
|
|
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
|
|
NimbusJwtDecoder jwtDecoder = (NimbusJwtDecoder)
|
|
@@ -941,7 +967,7 @@ JwtDecoder jwtDecoder() {
|
|
|
|
|
|
return jwtDecoder;
|
|
return jwtDecoder;
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-jwt-claimsetmapping]]
|
|
[[oauth2resourceserver-jwt-claimsetmapping]]
|
|
=== Configuring Claim Set Mapping
|
|
=== Configuring Claim Set Mapping
|
|
@@ -1075,7 +1101,8 @@ First, include the needed dependencies and second, indicate the introspection en
|
|
|
|
|
|
To specify where the introspection endpoint is, simply do:
|
|
To specify where the introspection endpoint is, simply do:
|
|
|
|
|
|
-```yaml
|
|
|
|
|
|
+[source,yaml]
|
|
|
|
+----
|
|
security:
|
|
security:
|
|
oauth2:
|
|
oauth2:
|
|
resourceserver:
|
|
resourceserver:
|
|
@@ -1083,7 +1110,7 @@ security:
|
|
introspection-uri: https://idp.example.com/introspect
|
|
introspection-uri: https://idp.example.com/introspect
|
|
client-id: client
|
|
client-id: client
|
|
client-secret: secret
|
|
client-secret: secret
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Where `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.
|
|
Where `https://idp.example.com/introspect` is the introspection endpoint hosted by your authorization server and `client-id` and `client-secret` are the credentials needed to hit that endpoint.
|
|
|
|
|
|
@@ -1133,21 +1160,23 @@ Once a token is authenticated, an instance of `BearerTokenAuthentication` is set
|
|
|
|
|
|
This means that it's available in `@Controller` methods when using `@EnableWebMvc` in your configuration:
|
|
This means that it's available in `@Controller` methods when using `@EnableWebMvc` in your configuration:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@GetMapping("/foo")
|
|
@GetMapping("/foo")
|
|
public String foo(BearerTokenAuthentication authentication) {
|
|
public String foo(BearerTokenAuthentication authentication) {
|
|
return authentication.getTokenAttributes().get("sub") + " is the subject";
|
|
return authentication.getTokenAttributes().get("sub") + " is the subject";
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Since `BearerTokenAuthentication` holds an `OAuth2AuthenticatedPrincipal`, that also means that it's available to controller methods, too:
|
|
Since `BearerTokenAuthentication` holds an `OAuth2AuthenticatedPrincipal`, that also means that it's available to controller methods, too:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@GetMapping("/foo")
|
|
@GetMapping("/foo")
|
|
public String foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {
|
|
public String foo(@AuthenticationPrincipal OAuth2AuthenticatedPrincipal principal) {
|
|
return principal.getAttribute("sub") + " is the subject";
|
|
return principal.getAttribute("sub") + " is the subject";
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
==== Looking Up Attributes Via SpEL
|
|
==== Looking Up Attributes Via SpEL
|
|
|
|
|
|
@@ -1170,7 +1199,8 @@ There are two `@Bean` s that Spring Boot generates on Resource Server's behalf.
|
|
The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server.
|
|
The first is a `WebSecurityConfigurerAdapter` that configures the app as a resource server.
|
|
When use Opaque Token, this `WebSecurityConfigurerAdapter` looks like:
|
|
When use Opaque Token, this `WebSecurityConfigurerAdapter` looks like:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
http
|
|
http
|
|
.authorizeRequests()
|
|
.authorizeRequests()
|
|
@@ -1178,13 +1208,14 @@ protected void configure(HttpSecurity http) {
|
|
.and()
|
|
.and()
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken)
|
|
.oauth2ResourceServer(OAuth2ResourceServerConfigurer::opaqueToken)
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
|
|
If the application doesn't expose a `WebSecurityConfigurerAdapter` bean, then Spring Boot will expose the above default one.
|
|
|
|
|
|
Replacing this is as simple as exposing the bean within the application:
|
|
Replacing this is as simple as exposing the bean within the application:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|
public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -1198,7 +1229,7 @@ public class MyCustomSecurityConfiguration extends WebSecurityConfigurerAdapter
|
|
.introspector(myIntrospector());
|
|
.introspector(myIntrospector());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
|
|
The above requires the scope of `message:read` for any URL that starts with `/messages/`.
|
|
|
|
|
|
@@ -1206,12 +1237,13 @@ Methods on the `oauth2ResourceServer` DSL will also override or replace auto con
|
|
|
|
|
|
For example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`:
|
|
For example, the second `@Bean` Spring Boot creates is an `OpaqueTokenIntrospector`, which decodes `String` tokens into validated instances of `OAuth2AuthenticatedPrincipal`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public OpaqueTokenIntrospector introspector() {
|
|
public OpaqueTokenIntrospector introspector() {
|
|
return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
|
|
return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
If the application doesn't expose a `OpaqueTokenIntrospector` bean, then Spring Boot will expose the above default one.
|
|
If the application doesn't expose a `OpaqueTokenIntrospector` bean, then Spring Boot will expose the above default one.
|
|
|
|
|
|
@@ -1222,7 +1254,8 @@ And its configuration can be overridden using `introspectionUri()` and `introspe
|
|
|
|
|
|
An authorization server's Introspection Uri can be configured <<oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:
|
|
An authorization server's Introspection Uri can be configured <<oauth2resourceserver-opaque-introspectionuri,as a configuration property>> or it can be supplied in the DSL:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -1236,7 +1269,7 @@ public class DirectlyConfiguredIntrospectionUri extends WebSecurityConfigurerAda
|
|
.introspectionClientCredentials("client", "secret");
|
|
.introspectionClientCredentials("client", "secret");
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Using `introspectionUri()` takes precedence over any configuration property.
|
|
Using `introspectionUri()` takes precedence over any configuration property.
|
|
|
|
|
|
@@ -1245,7 +1278,8 @@ Using `introspectionUri()` takes precedence over any configuration property.
|
|
|
|
|
|
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of `OpaqueTokenIntrospector`:
|
|
More powerful than `introspectionUri()` is `introspector()`, which will completely replace any Boot auto configuration of `OpaqueTokenIntrospector`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@EnableWebSecurity
|
|
@EnableWebSecurity
|
|
public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter {
|
|
public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter {
|
|
protected void configure(HttpSecurity http) {
|
|
protected void configure(HttpSecurity http) {
|
|
@@ -1258,7 +1292,7 @@ public class DirectlyConfiguredIntrospector extends WebSecurityConfigurerAdapter
|
|
.introspector(myCustomIntrospector());
|
|
.introspector(myCustomIntrospector());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
This is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.
|
|
This is handy when deeper configuration, like <<oauth2resourceserver-opaque-authorization-extraction,authority mapping>>, <<oauth2resourceserver-opaque-jwt-introspector,JWT revocation>>, or <<oauth2resourceserver-opaque-timeouts,request timeouts>>, is necessary.
|
|
|
|
|
|
@@ -1267,12 +1301,13 @@ This is handy when deeper configuration, like <<oauth2resourceserver-opaque-auth
|
|
|
|
|
|
Or, exposing a `OpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:
|
|
Or, exposing a `OpaqueTokenIntrospector` `@Bean` has the same effect as `introspector()`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public OpaqueTokenIntrospector introspector() {
|
|
public OpaqueTokenIntrospector introspector() {
|
|
return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
|
|
return new NimbusOpaqueTokenIntrospector(introspectionUri, clientId, clientSecret);
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-opaque-authorization]]
|
|
[[oauth2resourceserver-opaque-authorization]]
|
|
=== Configuring Authorization
|
|
=== Configuring Authorization
|
|
@@ -1314,18 +1349,20 @@ By default, Opaque Token support will extract the scope claim from an introspect
|
|
|
|
|
|
For example, if the introspection response were:
|
|
For example, if the introspection response were:
|
|
|
|
|
|
-```json
|
|
|
|
|
|
+[source,json]
|
|
|
|
+----
|
|
{
|
|
{
|
|
"active" : true,
|
|
"active" : true,
|
|
"scope" : "message:read message:write"
|
|
"scope" : "message:read message:write"
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Then Resource Server would generate an `Authentication` with two authorities, one for `message:read` and the other for `message:write`.
|
|
Then Resource Server would generate an `Authentication` with two authorities, one for `message:read` and the other for `message:write`.
|
|
|
|
|
|
This can, of course, be customized using a custom `OpaqueTokenIntrospector` that takes a look at the attribute set and converts in its own way:
|
|
This can, of course, be customized using a custom `OpaqueTokenIntrospector` that takes a look at the attribute set and converts in its own way:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
private OpaqueTokenIntrospector delegate =
|
|
private OpaqueTokenIntrospector delegate =
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
@@ -1343,16 +1380,17 @@ public class CustomAuthoritiesOpaqueTokenIntrospector implements OpaqueTokenIntr
|
|
.collect(Collectors.toList());
|
|
.collect(Collectors.toList());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
|
|
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public OpaqueTokenIntrospector introspector() {
|
|
public OpaqueTokenIntrospector introspector() {
|
|
return new CustomAuthoritiesOpaqueTokenIntrospector();
|
|
return new CustomAuthoritiesOpaqueTokenIntrospector();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-opaque-timeouts]]
|
|
[[oauth2resourceserver-opaque-timeouts]]
|
|
=== Configuring Timeouts
|
|
=== Configuring Timeouts
|
|
@@ -1387,7 +1425,8 @@ So, let's say that you've got a requirement that requires you to check with the
|
|
|
|
|
|
Even though you are using the JWT format for the token, your validation method is introspection, meaning you'd want to do:
|
|
Even though you are using the JWT format for the token, your validation method is introspection, meaning you'd want to do:
|
|
|
|
|
|
-```yaml
|
|
|
|
|
|
+[source,yaml]
|
|
|
|
+----
|
|
spring:
|
|
spring:
|
|
security:
|
|
security:
|
|
oauth2:
|
|
oauth2:
|
|
@@ -1396,7 +1435,7 @@ spring:
|
|
introspection-uri: https://idp.example.org/introspection
|
|
introspection-uri: https://idp.example.org/introspection
|
|
client-id: client
|
|
client-id: client
|
|
client-secret: secret
|
|
client-secret: secret
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
In this case, the resulting `Authentication` would be `BearerTokenAuthentication`.
|
|
In this case, the resulting `Authentication` would be `BearerTokenAuthentication`.
|
|
Any attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be whatever was returned by the introspection endpoint.
|
|
Any attributes in the corresponding `OAuth2AuthenticatedPrincipal` would be whatever was returned by the introspection endpoint.
|
|
@@ -1406,7 +1445,8 @@ Now what?
|
|
|
|
|
|
In this case, you can create a custom `OpaqueTokenIntrospector` that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
|
|
In this case, you can create a custom `OpaqueTokenIntrospector` that still hits the endpoint, but then updates the returned principal to have the JWTs claims as the attributes:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
private OpaqueTokenIntrospector delegate =
|
|
private OpaqueTokenIntrospector delegate =
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
@@ -1429,16 +1469,17 @@ public class JwtOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
|
|
Thereafter, this custom introspector can be configured simply by exposing it as a `@Bean`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
public OpaqueTokenIntrospector introspector() {
|
|
public OpaqueTokenIntrospector introspector() {
|
|
return new JwtOpaqueTokenIntropsector();
|
|
return new JwtOpaqueTokenIntropsector();
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
[[oauth2resourceserver-opaque-userinfo]]
|
|
[[oauth2resourceserver-opaque-userinfo]]
|
|
=== Calling a `/userinfo` Endpoint
|
|
=== Calling a `/userinfo` Endpoint
|
|
@@ -1454,7 +1495,8 @@ This implementation below does three things:
|
|
* Looks up the appropriate client registration associated with the `/userinfo` endpoint
|
|
* Looks up the appropriate client registration associated with the `/userinfo` endpoint
|
|
* Invokes and returns the response from the `/userinfo` endpoint
|
|
* Invokes and returns the response from the `/userinfo` endpoint
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
private final OpaqueTokenIntrospector delegate =
|
|
private final OpaqueTokenIntrospector delegate =
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
@@ -1475,12 +1517,13 @@ public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector
|
|
return this.oauth2UserService.loadUser(oauth2UserRequest);
|
|
return this.oauth2UserService.loadUser(oauth2UserRequest);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
If you aren't using `spring-security-oauth2-client`, it's still quite simple.
|
|
If you aren't using `spring-security-oauth2-client`, it's still quite simple.
|
|
You will simply need to invoke the `/userinfo` with your own instance of `WebClient`:
|
|
You will simply need to invoke the `/userinfo` with your own instance of `WebClient`:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector {
|
|
private final OpaqueTokenIntrospector delegate =
|
|
private final OpaqueTokenIntrospector delegate =
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
new NimbusOpaqueTokenIntrospector("https://idp.example.org/introspect", "client", "secret");
|
|
@@ -1492,19 +1535,17 @@ public class UserInfoOpaqueTokenIntrospector implements OpaqueTokenIntrospector
|
|
return makeUserInfoRequest(authorized);
|
|
return makeUserInfoRequest(authorized);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
-```
|
|
|
|
|
|
+----
|
|
|
|
|
|
Either way, having created your `OpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:
|
|
Either way, having created your `OpaqueTokenIntrospector`, you should publish it as a `@Bean` to override the defaults:
|
|
|
|
|
|
-```java
|
|
|
|
|
|
+[source,java]
|
|
|
|
+----
|
|
@Bean
|
|
@Bean
|
|
OpaqueTokenIntrospector introspector() {
|
|
OpaqueTokenIntrospector introspector() {
|
|
return new UserInfoOpaqueTokenIntrospector(...);
|
|
return new UserInfoOpaqueTokenIntrospector(...);
|
|
}
|
|
}
|
|
-```
|
|
|
|
-
|
|
|
|
-[[jc-authentication]]
|
|
|
|
-== Authentication
|
|
|
|
|
|
+----
|
|
|
|
|
|
Thus far we have only taken a look at the most basic authentication configuration.
|
|
Thus far we have only taken a look at the most basic authentication configuration.
|
|
Let's take a look at a few slightly more advanced options for configuring authentication.
|
|
Let's take a look at a few slightly more advanced options for configuring authentication.
|