Update ref doc for oauth2-client WebClient integration

Fixes gh-7404
This commit is contained in:
Joe Grandja 2019-09-19 20:06:37 -04:00
parent 38e87568a6
commit 52f0e5287b
4 changed files with 132 additions and 105 deletions

View File

@ -12,7 +12,7 @@ Below are the highlights of the release.
** `authorization_code` grant support
** `client_credentials` grant support
* OAuth 2.0 Resource Server - support for {gh-samples-url}/boot/oauth2resourceserver[JWT-encoded bearer tokens]
* Added OAuth2 <<servlet-webclient,WebClient>> integration
* Added OAuth2 <<oauth2Client-webclient-servlet,WebClient>> integration
* <<request-matching,HTTP Firewall>> protects against HTTP Verb Tampering and Cross-site Tracing
* <<exception-translation-filter,ExceptionTranslationFilter>> support for selecting an `AccessDeniedHandler` by `RequestMatcher`
* <<csrf,CSRF>> support for excluding certain requests

View File

@ -3,7 +3,7 @@
[NOTE]
====
The following documentation is for use within Reactive environments.
For Servlet environments, refer to <<servlet-webclient, WebClient for Servlet>> environments.
For Servlet environments, refer to <<oauth2Client-webclient-servlet, WebClient for Servlet>> environments.
====

View File

@ -1,102 +0,0 @@
[[servlet-webclient]]
== WebClient for Servlet Environments
[NOTE]
====
The following documentation is for use within Servlet environments.
For all other environments, refer to <<webclient, WebClient for Reactive>> environments.
====
Spring Framework has built in support for setting a Bearer token.
[source,java]
----
webClient.get()
.headers(h -> h.setBearerAuth(token))
...
----
Spring Security builds on this support to provide additional benefits:
* Spring Security will automatically refresh expired tokens (if a refresh token is present)
* If an access token is requested and not present, Spring Security will automatically request the access token.
** For authorization_code this involves performing the redirect and then replaying the original request
** For client_credentials the token is simply requested and saved
* Support for the ability to transparently include the current OAuth token or explicitly select which token should be used.
[[servlet-webclient-setup]]
=== WebClient OAuth2 Setup
The first step is ensuring to setup the `WebClient` correctly.
An example of setting up `WebClient` in a servlet environment can be found below:
[source,java]
----
@Bean
WebClient webClient(ClientRegistrationRepository clientRegistrations,
OAuth2AuthorizedClientRepository authorizedClients) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
// oauth.setDefaultOAuth2AuthorizedClient(true);
// (optional) set a default ClientRegistration.registrationId
// oauth.setDefaultClientRegistrationId("client-registration-id");
return WebClient.builder()
.apply(oauth2.oauth2Configuration())
.build();
}
----
[[servlet-webclient-implicit]]
=== Implicit OAuth2AuthorizedClient
If we set `defaultOAuth2AuthorizedClient` to `true` in our setup and the user authenticated with oauth2Login (i.e. OIDC), then the current authentication is used to automatically provide the access token.
Alternatively, if we set `defaultClientRegistrationId` to a valid `ClientRegistration` id, that registration is used to provide the access token.
This is convenient, but in environments where not all endpoints should get the access token, it is dangerous (you might provide the wrong access token to an endpoint).
[source,java]
----
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.retrieve()
.bodyToMono(String.class);
----
[[servlet-webclient-explicit]]
=== Explicit OAuth2AuthorizedClient
The `OAuth2AuthorizedClient` can be explicitly provided by setting it on the request attributes.
In the example below we resolve the `OAuth2AuthorizedClient` using Spring WebFlux or Spring MVC argument resolver support.
However, it does not matter how the `OAuth2AuthorizedClient` is resolved.
[source,java]
----
@GetMapping("/explicit")
Mono<String> explicit(@RegisteredOAuth2AuthorizedClient("client-id") OAuth2AuthorizedClient authorizedClient) {
return this.webClient
.get()
.uri(this.uri)
.attributes(oauth2AuthorizedClient(authorizedClient))
.retrieve()
.bodyToMono(String.class);
}
----
[[servlet-webclient-clientregistrationid]]
=== clientRegistrationId
Alternatively, it is possible to specify the `clientRegistrationId` on the request attributes and the `WebClient` will attempt to lookup the `OAuth2AuthorizedClient`.
If it is not found, one will automatically be acquired.
[source,java]
----
Mono<String> body = this.webClient
.get()
.uri(this.uri)
.attributes(clientRegistrationId("client-id"))
.retrieve()
.bodyToMono(String.class);
----

View File

@ -12,7 +12,7 @@ At a high-level, the core features available are:
* https://tools.ietf.org/html/rfc6749#section-1.3.3[Resource Owner Password Credentials]
.HTTP Client support
* <<servlet-webclient, `WebClient` integration for Servlet Environments>> (for requesting protected resources)
* <<oauth2Client-webclient-servlet, `WebClient` integration for Servlet Environments>> (for requesting protected resources)
The `HttpSecurity.oauth2Client()` DSL provides a number of configuration options for customizing the core components used by OAuth 2.0 Client.
In addition, `HttpSecurity.oauth2Client().authorizationCodeGrant()` enables the customization of the Authorization Code grant.
@ -87,6 +87,7 @@ The following sections will go into more detail on the core components used by O
** <<oauth2Client-password-grant, Resource Owner Password Credentials>>
* <<oauth2Client-additional-features>>
** <<oauth2Client-registered-authorized-client, Resolving an Authorized Client>>
* <<oauth2Client-webclient-servlet>>
[[oauth2Client-core-interface-class]]
@ -1058,3 +1059,131 @@ The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2Authoriz
** `client_credentials` - the access token is obtained directly from the Token Endpoint
** `password` - the access token is obtained directly from the Token Endpoint
* If the `OAuth2AccessToken` is expired, it will be renewed (or refreshed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorization
[[oauth2Client-webclient-servlet]]
=== WebClient integration for Servlet Environments
The OAuth 2.0 Client support integrates with `WebClient` using an `ExchangeFilterFunction`.
The `ServletOAuth2AuthorizedClientExchangeFilterFunction` provides a simple mechanism for requesting protected resources by using an `OAuth2AuthorizedClient` and including the associated `OAuth2AccessToken` as a Bearer Token.
It directly uses an <<oauth2Client-authorized-manager-provider, OAuth2AuthorizedClientManager>> and therefore inherits the following capabilities:
* An `OAuth2AccessToken` will be requested if the client has not yet been authorized.
** `authorization_code` - triggers the Authorization Request redirect to initiate the flow
** `client_credentials` - the access token is obtained directly from the Token Endpoint
** `password` - the access token is obtained directly from the Token Endpoint
* If the `OAuth2AccessToken` is expired, it will be refreshed (or renewed) if an `OAuth2AuthorizedClientProvider` is available to perform the authorization
The following code shows an example of how to configure `WebClient` with OAuth 2.0 Client support:
[source,java]
----
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
----
[NOTE]
Spring Boot 2.x auto-configuration registers an `OAuth2AuthorizedClientManager` `@Bean` in the `ApplicationContext`.
==== Providing the Authorized Client
The `ServletOAuth2AuthorizedClientExchangeFilterFunction` determines the client to use (for a request) by resolving the `OAuth2AuthorizedClient` from the `ClientRequest.attributes()` (request attributes).
The following code shows how to set an `OAuth2AuthorizedClient` as a request attribute:
[source,java]
----
@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
String resourceUri = ...
String body = webClient
.get()
.uri(resourceUri)
.attributes(oauth2AuthorizedClient(authorizedClient)) <1>
.retrieve()
.bodyToMono(String.class)
.block();
...
return "index";
}
----
<1> `oauth2AuthorizedClient()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
The following code shows how to set the `ClientRegistration.getRegistrationId()` as a request attribute:
[source,java]
----
@GetMapping("/")
public String index() {
String resourceUri = ...
String body = webClient
.get()
.uri(resourceUri)
.attributes(clientRegistrationId("okta")) <1>
.retrieve()
.bodyToMono(String.class)
.block();
...
return "index";
}
----
<1> `clientRegistrationId()` is a `static` method in `ServletOAuth2AuthorizedClientExchangeFilterFunction`.
==== Defaulting the Authorized Client
If neither `OAuth2AuthorizedClient` or `ClientRegistration.getRegistrationId()` is provided as a request attribute, the `ServletOAuth2AuthorizedClientExchangeFilterFunction` can determine the _default_ client to use depending on it's configuration.
If `setDefaultOAuth2AuthorizedClient(true)` is configured and the user has authenticated using `HttpSecurity.oauth2Login()`, the `OAuth2AccessToken` associated with the current `OAuth2AuthenticationToken` is used.
The following code shows the specific configuration:
[source,java]
----
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultOAuth2AuthorizedClient(true);
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
----
[WARNING]
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.
Alternatively, if `setDefaultClientRegistrationId("okta")` is configured with a valid `ClientRegistration`, the `OAuth2AccessToken` associated with the `OAuth2AuthorizedClient` is used.
The following code shows the specific configuration:
[source,java]
----
@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
oauth2Client.setDefaultClientRegistrationId("okta");
return WebClient.builder()
.apply(oauth2Client.oauth2Configuration())
.build();
}
----
[WARNING]
It is recommended to be cautious with this feature since all HTTP requests will receive the access token.