mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-11-04 00:28:54 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			313 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
			
		
		
	
	
			313 lines
		
	
	
		
			8.6 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
= OAuth 2.0 Bearer Tokens
 | 
						|
 | 
						|
[[oauth2resourceserver-bearertoken-resolver]]
 | 
						|
== Bearer Token Resolution
 | 
						|
 | 
						|
By default, Resource Server looks for a bearer token in the `Authorization` header.
 | 
						|
This, however, can be customized in a handful of ways.
 | 
						|
 | 
						|
=== Reading the Bearer Token from a Custom Header
 | 
						|
 | 
						|
For example, you may have a need to read the bearer token from a custom header.
 | 
						|
To achieve this, you can expose a `DefaultBearerTokenResolver` as a bean, or wire an instance into the DSL, as you can see in the following example:
 | 
						|
 | 
						|
.Custom Bearer Token Header
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
BearerTokenResolver bearerTokenResolver() {
 | 
						|
    DefaultBearerTokenResolver bearerTokenResolver = new DefaultBearerTokenResolver();
 | 
						|
    bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION);
 | 
						|
    return bearerTokenResolver;
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
fun bearerTokenResolver(): BearerTokenResolver {
 | 
						|
    val bearerTokenResolver = DefaultBearerTokenResolver()
 | 
						|
    bearerTokenResolver.setBearerTokenHeaderName(HttpHeaders.PROXY_AUTHORIZATION)
 | 
						|
    return bearerTokenResolver
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Xml::
 | 
						|
+
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<http>
 | 
						|
    <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
 | 
						|
</http>
 | 
						|
 | 
						|
<bean id="bearerTokenResolver"
 | 
						|
        class="org.springframework.security.oauth2.server.resource.web.DefaultBearerTokenResolver">
 | 
						|
    <property name="bearerTokenHeaderName" value="Proxy-Authorization"/>
 | 
						|
</bean>
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Or, in circumstances where a provider is using both a custom header and value, you can use `HeaderBearerTokenResolver` instead.
 | 
						|
 | 
						|
=== Reading the Bearer Token from a Form Parameter
 | 
						|
 | 
						|
Or, you may wish to read the token from a form parameter, which you can do by configuring the `DefaultBearerTokenResolver`, as you can see below:
 | 
						|
 | 
						|
.Form Parameter Bearer Token
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
DefaultBearerTokenResolver resolver = new DefaultBearerTokenResolver();
 | 
						|
resolver.setAllowFormEncodedBodyParameter(true);
 | 
						|
http
 | 
						|
    .oauth2ResourceServer(oauth2 -> oauth2
 | 
						|
        .bearerTokenResolver(resolver)
 | 
						|
    );
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
val resolver = DefaultBearerTokenResolver()
 | 
						|
resolver.setAllowFormEncodedBodyParameter(true)
 | 
						|
http {
 | 
						|
    oauth2ResourceServer {
 | 
						|
        bearerTokenResolver = resolver
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Xml::
 | 
						|
+
 | 
						|
[source,xml,role="secondary"]
 | 
						|
----
 | 
						|
<http>
 | 
						|
    <oauth2-resource-server bearer-token-resolver-ref="bearerTokenResolver"/>
 | 
						|
</http>
 | 
						|
 | 
						|
<bean id="bearerTokenResolver"
 | 
						|
        class="org.springframework.security.oauth2.server.resource.web.HeaderBearerTokenResolver">
 | 
						|
    <property name="allowFormEncodedBodyParameter" value="true"/>
 | 
						|
</bean>
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
== Bearer Token Propagation
 | 
						|
 | 
						|
Now that your resource server has validated the token, it might be handy to pass it to downstream services.
 | 
						|
This is quite simple with javadoc:org.springframework.security.oauth2.server.resource.web.reactive.function.client.ServletBearerExchangeFilterFunction[], which you can see in the following example:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
public WebClient rest() {
 | 
						|
    return WebClient.builder()
 | 
						|
            .filter(new ServletBearerExchangeFilterFunction())
 | 
						|
            .build();
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
fun rest(): WebClient {
 | 
						|
    return WebClient.builder()
 | 
						|
            .filter(ServletBearerExchangeFilterFunction())
 | 
						|
            .build()
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
When the above `WebClient` is used to perform requests, Spring Security will look up the current `Authentication` and extract any javadoc:org.springframework.security.oauth2.core.AbstractOAuth2Token[] credential.
 | 
						|
Then, it will propagate that token in the `Authorization` header.
 | 
						|
 | 
						|
For example:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
this.rest.get()
 | 
						|
        .uri("https://other-service.example.com/endpoint")
 | 
						|
        .retrieve()
 | 
						|
        .bodyToMono(String.class)
 | 
						|
        .block()
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
this.rest.get()
 | 
						|
        .uri("https://other-service.example.com/endpoint")
 | 
						|
        .retrieve()
 | 
						|
        .bodyToMono<String>()
 | 
						|
        .block()
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
Will invoke the `https://other-service.example.com/endpoint`, adding the bearer token `Authorization` header for you.
 | 
						|
 | 
						|
In places where you need to override this behavior, it's a simple matter of supplying the header yourself, like so:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
this.rest.get()
 | 
						|
        .uri("https://other-service.example.com/endpoint")
 | 
						|
        .headers(headers -> headers.setBearerAuth(overridingToken))
 | 
						|
        .retrieve()
 | 
						|
        .bodyToMono(String.class)
 | 
						|
        .block()
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
this.rest.get()
 | 
						|
        .uri("https://other-service.example.com/endpoint")
 | 
						|
        .headers{  headers -> headers.setBearerAuth(overridingToken)}
 | 
						|
        .retrieve()
 | 
						|
        .bodyToMono<String>()
 | 
						|
        .block()
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
In this case, the filter will fall back and simply forward the request onto the rest of the web filter chain.
 | 
						|
 | 
						|
[NOTE]
 | 
						|
Unlike the javadoc:org.springframework.security.oauth2.client.web.reactive.function.client.ServletOAuth2AuthorizedClientExchangeFilterFunction[OAuth 2.0 Client filter function], this filter function makes no attempt to renew the token, should it be expired.
 | 
						|
To obtain this level of support, please use the OAuth 2.0 Client filter.
 | 
						|
 | 
						|
=== `RestTemplate` support
 | 
						|
 | 
						|
There is no `RestTemplate` equivalent for `ServletBearerExchangeFilterFunction` at the moment, but you can propagate the request's bearer token quite simply with your own interceptor:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
RestTemplate rest() {
 | 
						|
	RestTemplate rest = new RestTemplate();
 | 
						|
	rest.getInterceptors().add((request, body, execution) -> {
 | 
						|
		Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
 | 
						|
		if (authentication == null) {
 | 
						|
			return execution.execute(request, body);
 | 
						|
		}
 | 
						|
 | 
						|
		if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
 | 
						|
			return execution.execute(request, body);
 | 
						|
		}
 | 
						|
 | 
						|
		AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
 | 
						|
	    request.getHeaders().setBearerAuth(token.getTokenValue());
 | 
						|
	    return execution.execute(request, body);
 | 
						|
	});
 | 
						|
	return rest;
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Bean
 | 
						|
fun rest(): RestTemplate {
 | 
						|
    val rest = RestTemplate()
 | 
						|
    rest.interceptors.add(ClientHttpRequestInterceptor { request, body, execution ->
 | 
						|
        val authentication: Authentication? = SecurityContextHolder.getContext().authentication
 | 
						|
        if (authentication == null) {
 | 
						|
            return execution.execute(request, body)
 | 
						|
        }
 | 
						|
 | 
						|
        if (authentication.credentials !is AbstractOAuth2Token) {
 | 
						|
            return execution.execute(request, body)
 | 
						|
        }
 | 
						|
 | 
						|
        request.headers.setBearerAuth(authentication.credentials.tokenValue)
 | 
						|
        execution.execute(request, body)
 | 
						|
    })
 | 
						|
    return rest
 | 
						|
}
 | 
						|
----
 | 
						|
======
 | 
						|
 | 
						|
 | 
						|
[NOTE]
 | 
						|
Unlike the javadoc:org.springframework.security.oauth2.client.OAuth2AuthorizedClientManager[OAuth 2.0 Authorized Client Manager], this filter interceptor makes no attempt to renew the token, should it be expired.
 | 
						|
To obtain this level of support, please create an interceptor using the xref:servlet/oauth2/client/index.adoc#oauth2client[OAuth 2.0 Authorized Client Manager].
 | 
						|
 | 
						|
[[oauth2resourceserver-bearertoken-failure]]
 | 
						|
== Bearer Token Failure
 | 
						|
 | 
						|
A bearer token may be invalid for a number of reasons. For example, the token may no longer be active.
 | 
						|
 | 
						|
In these circumstances, Resource Server throws an `InvalidBearerTokenException`.
 | 
						|
Like other exceptions, this results in an OAuth 2.0 Bearer Token error response:
 | 
						|
 | 
						|
[source,http request]
 | 
						|
----
 | 
						|
HTTP/1.1 401 Unauthorized
 | 
						|
WWW-Authenticate: Bearer error_code="invalid_token", error_description="Unsupported algorithm of none", error_uri="https://tools.ietf.org/html/rfc6750#section-3.1"
 | 
						|
----
 | 
						|
 | 
						|
Additionally, it is published as an `AuthenticationFailureBadCredentialsEvent`, which you can xref:servlet/authentication/events.adoc#servlet-events[listen for in your application] like so:
 | 
						|
 | 
						|
[tabs]
 | 
						|
======
 | 
						|
Java::
 | 
						|
+
 | 
						|
[source,java,role="primary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
public class FailureEvents {
 | 
						|
	@EventListener
 | 
						|
    public void onFailure(AuthenticationFailureBadCredentialsEvent badCredentials) {
 | 
						|
		if (badCredentials.getAuthentication() instanceof BearerTokenAuthenticationToken) {
 | 
						|
		    // ... handle
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
 | 
						|
Kotlin::
 | 
						|
+
 | 
						|
[source,kotlin,role="secondary"]
 | 
						|
----
 | 
						|
@Component
 | 
						|
class FailureEvents {
 | 
						|
    @EventListener
 | 
						|
    fun onFailure(badCredentials: AuthenticationFailureBadCredentialsEvent) {
 | 
						|
        if (badCredentials.authentication is BearerTokenAuthenticationToken) {
 | 
						|
            // ... handle
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 | 
						|
----
 | 
						|
======
 |