Browse Source

Add WebClient OAuth2 Documentation

Fixes: gh-5859
Rob Winch 7 years ago
parent
commit
735d755bb1

+ 1 - 1
docs/manual/src/docs/asciidoc/_includes/preface/whats-new.adoc

@@ -39,7 +39,7 @@ For example, `@WithMockUser(setupBefore = TestExecutionEvent.TEST_EXECUTION)` wi
 ** Support for CORS was added for WebFlux (already supported in Servlets)
 * Redirecting to HTTPS
 ** Support for HTTPS redirect was added
-* Web Client
+* WebClient + OAuth2 Support for <<servlet-webclient,Servlet>> and <<webclient,Reactive>> environments
 * <<ldap>> - added support for setting up an `LdapContext` from custom environment variables
 * <<x509>> - added support for deriving the X.509 principal via a strategy
 * The Look and Feel for the default login and logout pages was modernized

+ 2 - 0
docs/manual/src/docs/asciidoc/_includes/reactive/index.adoc

@@ -2,6 +2,8 @@
 
 include::webflux.adoc[leveloffset=+1]
 
+include::webclient.adoc[leveloffset=+1]
+
 include::method.adoc[leveloffset=+1]
 
 include::test.adoc[leveloffset=+1]

+ 97 - 0
docs/manual/src/docs/asciidoc/_includes/reactive/webclient.adoc

@@ -0,0 +1,97 @@
+= WebClient
+
+[NOTE]
+====
+The following documentation is for use within Reactive environments.
+For Servlet environments, refer to <<servlet-webclient, WebClient for Servlet>> 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.
+
+[[webclient-setup]]
+== WebClient OAuth2 Setup
+
+The first step is ensuring to setup the `WebClient` correctly.
+An example of setting up `WebClient` in a fully reactive environment can be found below:
+
+[source,java]
+----
+@Bean
+WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations,
+		ServerOAuth2AuthorizedClientRepository authorizedClients) {
+	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
+			new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
+	// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
+	oauth.setDefaultOAuth2AuthorizedClient(true);
+	return WebClient.builder()
+			.filter(oauth)
+			.build();
+}
+----
+
+[[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.
+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);
+----
+
+[[webclient-explicit]]
+== Explicit OAuth2AuthorizedClient
+
+The `OAuth2AuthorizedClient` can be explicitly provided by setting it on the requests 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);
+}
+----
+
+[[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);
+----

+ 2 - 0
docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/index.adoc

@@ -11,6 +11,8 @@ include::ldap.adoc[]
 
 include::oauth2.adoc[]
 
+include::webclient.adoc[]
+
 include::jsp-taglibs.adoc[]
 
 include::jaas.adoc[]

+ 99 - 0
docs/manual/src/docs/asciidoc/_includes/servlet/additional-topics/webclient.adoc

@@ -0,0 +1,99 @@
+[[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 fully reactive environment can be found below:
+
+[source,java]
+----
+@Bean
+WebClient webClient(ReactiveClientRegistrationRepository clientRegistrations,
+		ServerOAuth2AuthorizedClientRepository authorizedClients) {
+	ServerOAuth2AuthorizedClientExchangeFilterFunction oauth =
+			new ServerOAuth2AuthorizedClientExchangeFilterFunction(clientRegistrations, authorizedClients);
+	// (optional) explicitly opt into using the oauth2Login to provide an access token implicitly
+	oauth.setDefaultOAuth2AuthorizedClient(true);
+	return WebClient.builder()
+			.filter(oauth)
+			.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.
+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 requests 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);
+----