mirror of
				https://github.com/spring-projects/spring-security.git
				synced 2025-10-31 14:48:54 +00:00 
			
		
		
		
	
		
			
	
	
		
			265 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
		
		
			
		
	
	
			265 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			Plaintext
		
	
	
	
	
	
|  | [[oauth2Client-additional-features]] | ||
|  | = Authorized Client Features | ||
|  | 
 | ||
|  | [[oauth2Client-registered-authorized-client]] | ||
|  | == Resolving an Authorized Client | ||
|  | 
 | ||
|  | The `@RegisteredOAuth2AuthorizedClient` annotation provides the capability of resolving a method parameter to an argument value of type `OAuth2AuthorizedClient`. | ||
|  | This is a convenient alternative compared to accessing the `OAuth2AuthorizedClient` using the `OAuth2AuthorizedClientManager` or `OAuth2AuthorizedClientService`. | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @Controller | ||
|  | public class OAuth2ClientController { | ||
|  | 
 | ||
|  | 	@GetMapping("/") | ||
|  | 	public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) { | ||
|  | 		OAuth2AccessToken accessToken = authorizedClient.getAccessToken(); | ||
|  | 
 | ||
|  | 		... | ||
|  | 
 | ||
|  | 		return "index"; | ||
|  | 	} | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @Controller | ||
|  | class OAuth2ClientController { | ||
|  |     @GetMapping("/") | ||
|  |     fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String { | ||
|  |         val accessToken = authorizedClient.accessToken | ||
|  | 
 | ||
|  |         ... | ||
|  | 
 | ||
|  |         return "index" | ||
|  |     } | ||
|  | } | ||
|  | ---- | ||
|  | ==== | ||
|  | 
 | ||
|  | The `@RegisteredOAuth2AuthorizedClient` annotation is handled by `OAuth2AuthorizedClientArgumentResolver`, which directly uses an xref:servlet/oauth2/client/core.adoc#oauth2Client-authorized-manager-provider[`OAuth2AuthorizedClientManager`] and therefore inherits it's capabilities. | ||
|  | 
 | ||
|  | 
 | ||
|  | [[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 xref:servlet/oauth2/client/core.adoc#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: | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { | ||
|  | 	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = | ||
|  | 			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); | ||
|  | 	return WebClient.builder() | ||
|  | 			.apply(oauth2Client.oauth2Configuration()) | ||
|  | 			.build(); | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient { | ||
|  |     val oauth2Client = ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager) | ||
|  |     return WebClient.builder() | ||
|  |             .apply(oauth2Client.oauth2Configuration()) | ||
|  |             .build() | ||
|  | } | ||
|  | ---- | ||
|  | ==== | ||
|  | 
 | ||
|  | === 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: | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @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"; | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @GetMapping("/") | ||
|  | fun index(@RegisteredOAuth2AuthorizedClient("okta") authorizedClient: OAuth2AuthorizedClient): String { | ||
|  |     val resourceUri: String = ... | ||
|  |     val body: String = webClient | ||
|  |             .get() | ||
|  |             .uri(resourceUri) | ||
|  |             .attributes(oauth2AuthorizedClient(authorizedClient)) <1> | ||
|  |             .retrieve() | ||
|  |             .bodyToMono() | ||
|  |             .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: | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @GetMapping("/") | ||
|  | public String index() { | ||
|  | 	String resourceUri = ... | ||
|  | 
 | ||
|  | 	String body = webClient | ||
|  | 			.get() | ||
|  | 			.uri(resourceUri) | ||
|  | 			.attributes(clientRegistrationId("okta"))   <1> | ||
|  | 			.retrieve() | ||
|  | 			.bodyToMono(String.class) | ||
|  | 			.block(); | ||
|  | 
 | ||
|  | 	... | ||
|  | 
 | ||
|  | 	return "index"; | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @GetMapping("/") | ||
|  | fun index(): String { | ||
|  |     val resourceUri: String = ... | ||
|  | 
 | ||
|  |     val body: String = webClient | ||
|  |             .get() | ||
|  |             .uri(resourceUri) | ||
|  |             .attributes(clientRegistrationId("okta"))  <1> | ||
|  |             .retrieve() | ||
|  |             .bodyToMono() | ||
|  |             .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: | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { | ||
|  | 	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = | ||
|  | 			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); | ||
|  | 	oauth2Client.setDefaultOAuth2AuthorizedClient(true); | ||
|  | 	return WebClient.builder() | ||
|  | 			.apply(oauth2Client.oauth2Configuration()) | ||
|  | 			.build(); | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient { | ||
|  |     val oauth2Client = 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: | ||
|  | 
 | ||
|  | ==== | ||
|  | .Java | ||
|  | [source,java,role="primary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) { | ||
|  | 	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client = | ||
|  | 			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager); | ||
|  | 	oauth2Client.setDefaultClientRegistrationId("okta"); | ||
|  | 	return WebClient.builder() | ||
|  | 			.apply(oauth2Client.oauth2Configuration()) | ||
|  | 			.build(); | ||
|  | } | ||
|  | ---- | ||
|  | 
 | ||
|  | .Kotlin | ||
|  | [source,kotlin,role="secondary"] | ||
|  | ---- | ||
|  | @Bean | ||
|  | fun webClient(authorizedClientManager: OAuth2AuthorizedClientManager?): WebClient { | ||
|  |     val oauth2Client = 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. |