Authorization Grant Support

Spring Security 提供了对以下授权赠送类型的支持:

  • 授权代码

  • 密码

  • 客户凭据

  • 客户端断言

  • JWT 持有者

对于每种授权类型,都有一个专门的 OAuth2AuthorizedClientProvider 实现,负责处理授权过程的特定方面。

除了提供对授权赠送的支持之外,Spring Security 还提供了对以下内容的支持:

  • 刷新访问令牌

  • 自定义授权请求和响应

  • 存储授权请求和响应

  • 使用配置的 RestTemplate 配置授权代码令牌响应处理程序

此支持使开发人员能够轻松地将 OAuth 2.0 授权流程集成到他们的 Spring Boot 应用程序中。

本部分介绍 Spring Security 对授权赠送的支持。

This section describes Spring Security’s support for authorization grants.

Authorization Code

有关 Authorization Code 授权的更多详情,请参阅 OAuth 2.0 授权框架。

See the OAuth 2.0 Authorization Framework for further details on the Authorization Code grant.

Obtaining Authorization

有关授权码授权,请参阅 Authorization Request/Response 协议流程。

See the Authorization Request/Response protocol flow for the Authorization Code grant.

Initiating the Authorization Request

OAuth2AuthorizationRequestRedirectFilter 使用 OAuth2AuthorizationRequestResolver 来解析 OAuth2AuthorizationRequest 并通过将最终用户的用户代理重定向到授权服务器的授权端点,从而启动授权码授权流。

The OAuth2AuthorizationRequestRedirectFilter uses an OAuth2AuthorizationRequestResolver to resolve an OAuth2AuthorizationRequest and initiate the Authorization Code grant flow by redirecting the end-user’s user-agent to the Authorization Server’s Authorization Endpoint.

OAuth2AuthorizationRequestResolver 的主要作用是从提供的 Web 请求中解析 OAuth2AuthorizationRequest。默认实现 DefaultOAuth2AuthorizationRequestResolver 匹配(默认)路径 /oauth2/authorization/{registrationId},提取 registrationId,并使用它为关联的 ClientRegistration 构建 OAuth2AuthorizationRequest

The primary role of the OAuth2AuthorizationRequestResolver is to resolve an OAuth2AuthorizationRequest from the provided web request. The default implementation DefaultOAuth2AuthorizationRequestResolver matches on the (default) path /oauth2/authorization/{registrationId}, extracting the registrationId, and using it to build the OAuth2AuthorizationRequest for the associated ClientRegistration.

考虑 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Consider the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            scope: read, write
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

鉴于前述属性,带有基本路径 /oauth2/authorization/okta 的请求将由 OAuth2AuthorizationRequestRedirectFilter 发起授权请求重定向并最终启动授权码授权流。

Given the preceding properties, a request with the base path /oauth2/authorization/okta initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter and ultimately starts the Authorization Code grant flow.

AuthorizationCodeOAuth2AuthorizedClientProvider 是授权码授权的 OAuth2AuthorizedClientProvider 的实现,它还由 OAuth2AuthorizationRequestRedirectFilter 发起授权请求重定向。

The AuthorizationCodeOAuth2AuthorizedClientProvider is an implementation of OAuth2AuthorizedClientProvider for the Authorization Code grant, which also initiates the Authorization Request redirect by the OAuth2AuthorizationRequestRedirectFilter.

如果 OAuth 2.0 客户端是 Public Client,请按如下方式配置 OAuth 2.0 客户端注册:

If the OAuth 2.0 Client is a Public Client, configure the OAuth 2.0 Client registration as follows:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: none
            authorization-grant-type: authorization_code
            redirect-uri: "{baseUrl}/authorized/okta"
            ...

公有客户端得到 Proof Key for Code Exchange (PKCE) 的支持。如果客户端在不受信任的环境(例如原生应用程序或基于网络浏览器的应用程序)中运行,因此无法维持其凭据的机密性,则在满足以下条件时会自动使用 PKCE:

Public Clients are supported by using Proof Key for Code Exchange (PKCE). If the client is running in an untrusted environment (such as a native application or web browser-based application) and is therefore incapable of maintaining the confidentiality of its credentials, PKCE is automatically used when the following conditions are true:

  1. client-secret is omitted (or empty)

  2. client-authentication-method is set to none (ClientAuthenticationMethod.NONE)

如果 OAuth 2.0 提供方支持 Confidential Clients 的 PKCE,您可以(可选)使用 DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()) 来对其进行配置。

If the OAuth 2.0 Provider supports PKCE for Confidential Clients, you may (optionally) configure it using DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer(OAuth2AuthorizationRequestCustomizers.withPkce()).

DefaultOAuth2AuthorizationRequestResolver 还使用 UriComponentsBuilderredirect-uri 支持 URI 模板变量。

The DefaultOAuth2AuthorizationRequestResolver also supports URI template variables for the redirect-uri by using UriComponentsBuilder.

以下配置使用所有受支持的 URI 模板变量:

The following configuration uses all the supported URI template variables:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            ...
            redirect-uri: "{baseScheme}://{baseHost}{basePort}{basePath}/authorized/{registrationId}"
            ...

{baseUrl} 解析为 {baseScheme}://{baseHost}{basePort}{basePath}

{baseUrl} resolves to {baseScheme}://{baseHost}{basePort}{basePath}

使用 URI 模板变量配置 redirect-uri 在 OAuth 2.0 客户端在 Proxy Server 后面运行时特别有用。这样做可确保在扩展 redirect-uri 时使用 X-Forwarded-* 头。

Configuring the redirect-uri with URI template variables is especially useful when the OAuth 2.0 Client is running behind a Proxy Server. Doing so ensures that the X-Forwarded-* headers are used when expanding the redirect-uri.

Customizing the Authorization Request

OAuth2AuthorizationRequestResolver 可以实现的主要用例之一是能够使用 OAuth 2.0 授权框架中定义的标准参数之外的附加参数自定义授权请求。

One of the primary use cases an OAuth2AuthorizationRequestResolver can realize is the ability to customize the Authorization Request with additional parameters above the standard parameters defined in the OAuth 2.0 Authorization Framework.

例如,OpenID Connect 为 Authorization Code Flow 定义额外的 OAuth 2.0 请求参数,该请求参数从 OAuth 2.0 Authorization Framework 中定义的标准参数扩展而来。这些扩展参数之一是 prompt 参数。

For example, OpenID Connect defines additional OAuth 2.0 request parameters for the Authorization Code Flow extending from the standard parameters defined in the OAuth 2.0 Authorization Framework. One of those extended parameters is the prompt parameter.

prompt 参数是可选的。空格分隔、区分大小写的 ASCII 字符串值列表,用于指定授权服务器是否提示最终用户进行重新身份验证和同意。定义的值为:noneloginconsentselect_account

The prompt parameter is optional. Space delimited, case sensitive list of ASCII string values that specifies whether the Authorization Server prompts the End-User for re-authentication and consent. The defined values are: none, login, consent, and select_account.

以下示例显示如何使用 Consumer<OAuth2AuthorizationRequest.Builder> 配置 DefaultOAuth2AuthorizationRequestResolver,该 Consumer<OAuth2AuthorizationRequest.Builder> 通过包括请求参数 prompt=consent 来定制 oauth2Login() 的授权请求。

The following example shows how to configure the DefaultOAuth2AuthorizationRequestResolver with a Consumer<OAuth2AuthorizationRequest.Builder> that customizes the Authorization Request for oauth2Login(), by including the request parameter prompt=consent.

  • Java

  • Kotlin

@Configuration
@EnableWebSecurity
public class OAuth2LoginSecurityConfig {

	@Autowired
	private ClientRegistrationRepository clientRegistrationRepository;

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.authorizeHttpRequests(authorize -> authorize
				.anyRequest().authenticated()
			)
			.oauth2Login(oauth2 -> oauth2
				.authorizationEndpoint(authorization -> authorization
					.authorizationRequestResolver(
						authorizationRequestResolver(this.clientRegistrationRepository)
					)
				)
			);
		return http.build();
	}

	private OAuth2AuthorizationRequestResolver authorizationRequestResolver(
			ClientRegistrationRepository clientRegistrationRepository) {

		DefaultOAuth2AuthorizationRequestResolver authorizationRequestResolver =
				new DefaultOAuth2AuthorizationRequestResolver(
						clientRegistrationRepository, "/oauth2/authorization");
		authorizationRequestResolver.setAuthorizationRequestCustomizer(
				authorizationRequestCustomizer());

		return  authorizationRequestResolver;
	}

	private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
		return customizer -> customizer
					.additionalParameters(params -> params.put("prompt", "consent"));
	}
}
@Configuration
@EnableWebSecurity
class SecurityConfig {

    @Autowired
    private lateinit var customClientRegistrationRepository: ClientRegistrationRepository

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            authorizeRequests {
                authorize(anyRequest, authenticated)
            }
            oauth2Login {
                authorizationEndpoint {
                    authorizationRequestResolver = authorizationRequestResolver(customClientRegistrationRepository)
                }
            }
        }
        return http.build()
    }

    private fun authorizationRequestResolver(
            clientRegistrationRepository: ClientRegistrationRepository?): OAuth2AuthorizationRequestResolver? {
        val authorizationRequestResolver = DefaultOAuth2AuthorizationRequestResolver(
                clientRegistrationRepository, "/oauth2/authorization")
        authorizationRequestResolver.setAuthorizationRequestCustomizer(
                authorizationRequestCustomizer())
        return authorizationRequestResolver
    }

    private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
        return Consumer { customizer ->
            customizer
                    .additionalParameters { params -> params["prompt"] = "consent" }
        }
    }
}

对于附加请求参数始终对特定提供程序相同时的简单用例,您可以在 authorization-uri 属性中直接添加它。

For the simple use case where the additional request parameter is always the same for a specific provider, you can add it directly in the authorization-uri property.

例如,如果对提供程序 okta,请求参数 prompt 的值始终为 consent,则可以按如下方式进行配置:

For example, if the value for the request parameter prompt is always consent for the provider okta, you can configure it as follows:

spring:
  security:
    oauth2:
      client:
        provider:
          okta:
            authorization-uri: https://dev-1234.oktapreview.com/oauth2/v1/authorize?prompt=consent

前面的示例显示了在标准参数之上添加自定义参数的常见用例。或者,如果您的要求更高级,则可以通过覆盖 OAuth2AuthorizationRequest.authorizationRequestUri 属性来完全控制构建授权请求 URI。

The preceding example shows the common use case of adding a custom parameter on top of the standard parameters. Alternatively, if your requirements are more advanced, you can take full control in building the Authorization Request URI by overriding the OAuth2AuthorizationRequest.authorizationRequestUri property.

OAuth2AuthorizationRequest.Builder.build() 构建了 OAuth2AuthorizationRequest.authorizationRequestUri,它表示使用 application/x-www-form-urlencoded 格式的所有查询参数的授权请求 URI。

OAuth2AuthorizationRequest.Builder.build() constructs the OAuth2AuthorizationRequest.authorizationRequestUri, which represents the Authorization Request URI including all query parameters using the application/x-www-form-urlencoded format.

以下示例显示了前面示例中 authorizationRequestCustomizer() 的一个变体,相反,它覆盖了 OAuth2AuthorizationRequest.authorizationRequestUri 属性:

The following example shows a variation of authorizationRequestCustomizer() from the preceding example and instead overrides the OAuth2AuthorizationRequest.authorizationRequestUri property:

  • Java

  • Kotlin

private Consumer<OAuth2AuthorizationRequest.Builder> authorizationRequestCustomizer() {
	return customizer -> customizer
				.authorizationRequestUri(uriBuilder -> uriBuilder
					.queryParam("prompt", "consent").build());
}
private fun authorizationRequestCustomizer(): Consumer<OAuth2AuthorizationRequest.Builder> {
    return Consumer { customizer: OAuth2AuthorizationRequest.Builder ->
        customizer
                .authorizationRequestUri { uriBuilder: UriBuilder ->
                    uriBuilder
                            .queryParam("prompt", "consent").build()
                }
    }
}

Storing the Authorization Request

AuthorizationRequestRepository 负责从发起授权请求的时间到收到授权响应(回调)的时间对 OAuth2AuthorizationRequest 进行持久性处理。

The AuthorizationRequestRepository is responsible for the persistence of the OAuth2AuthorizationRequest from the time the Authorization Request is initiated to the time the Authorization Response is received (the callback).

OAuth2AuthorizationRequest 用于关联和验证授权响应。

The OAuth2AuthorizationRequest is used to correlate and validate the Authorization Response.

AuthorizationRequestRepository 的默认实现为 HttpSessionOAuth2AuthorizationRequestRepository,该实现将 OAuth2AuthorizationRequest 存储在 HttpSession 中。

The default implementation of AuthorizationRequestRepository is HttpSessionOAuth2AuthorizationRequestRepository, which stores the OAuth2AuthorizationRequest in the HttpSession.

如果您具有 AuthorizationRequestRepository 的自定义实现,则可以按如下方式进行配置:

If you have a custom implementation of AuthorizationRequestRepository, you can configure it as follows:

AuthorizationRequestRepository Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client(oauth2 -> oauth2
				.authorizationCodeGrant(codeGrant -> codeGrant
					.authorizationRequestRepository(this.authorizationRequestRepository())
					...
				)
            .oauth2Login(oauth2 -> oauth2
                .authorizationEndpoint(endpoint -> endpoint
                    .authorizationRequestRepository(this.authorizationRequestRepository())
                    ...
                )
            ).build();
	}

    @Bean
    public AuthorizationRequestRepository<OAuth2AuthorizationRequest> authorizationRequestRepository() {
        return new CustomOAuth2AuthorizationRequestRepository();
    }
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    authorizationRequestRepository = authorizationRequestRepository()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant authorization-request-repository-ref="authorizationRequestRepository"/>
	</oauth2-client>
</http>

Requesting an Access Token

有关授权码授权,请参阅 Access Token Request/Response 协议流程。

See the Access Token Request/Response protocol flow for the Authorization Code grant.

授权码授权的 OAuth2AccessTokenResponseClient 的默认实现为 DefaultAuthorizationCodeTokenResponseClient,它使用 RestOperations 实例在授权服务器令牌端点处将授权码交换为访问令牌。

The default implementation of OAuth2AccessTokenResponseClient for the Authorization Code grant is DefaultAuthorizationCodeTokenResponseClient, which uses a RestOperations instance to exchange an authorization code for an access token at the Authorization Server’s Token Endpoint.

DefaultAuthorizationCodeTokenResponseClient 十分灵活,因为它允许你自定义令牌请求预处理和/或令牌响应后处理。

The DefaultAuthorizationCodeTokenResponseClient is flexible, as it lets you customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,您可以为 DefaultAuthorizationCodeTokenResponseClient.setRequestEntityConverter() 提供自定义 Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>>。默认实施(OAuth2AuthorizationCodeGrantRequestEntityConverter)构建标准 OAuth 2.0 Access Token RequestRequestEntity 表示形式。但是,提供一个自定义 Converter 将让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide DefaultAuthorizationCodeTokenResponseClient.setRequestEntityConverter() with a custom Converter<OAuth2AuthorizationCodeGrantRequest, RequestEntity<?>>. The default implementation (OAuth2AuthorizationCodeGrantRequestEntityConverter) builds a RequestEntity representation of a standard OAuth 2.0 Access Token Request. However, providing a custom Converter would let you extend the standard Token Request and add custom parameter(s).

要仅自定义请求的参数,可以使用自定义 Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>OAuth2AuthorizationCodeGrantRequestEntityConverter.setParametersConverter() 提供,以完全覆盖随请求发送的参数。这通常比直接构建 RequestEntity 更简单。

To customize only the parameters of the request, you can provide OAuth2AuthorizationCodeGrantRequestEntityConverter.setParametersConverter() with a custom Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

如果你更愿意只添加其他参数,可以使用自定义 Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>>OAuth2AuthorizationCodeGrantRequestEntityConverter.addParametersConverter() 提供以构建聚合 Converter

If you prefer to only add additional parameters, you can provide OAuth2AuthorizationCodeGrantRequestEntityConverter.addParametersConverter() with a custom Converter<OAuth2AuthorizationCodeGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回 OAuth 2.0 访问令牌请求的有效 RequestEntity 表示,该访问令牌请求由目标 OAuth 2.0 提供商理解。

The custom Converter must return a valid RequestEntity representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果你需要自定义令牌响应后处理,则需要使用自定义配置的 RestOperationsDefaultAuthorizationCodeTokenResponseClient.setRestOperations() 提供。默认 RestOperations 配置如下:

On the other end, if you need to customize the post-handling of the Token Response, you need to provide DefaultAuthorizationCodeTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

需要 Spring MVC FormHttpMessageConverter ,因为在发送 OAuth 2.0 访问令牌请求时会使用它。

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter 是 OAuth 2.0 访问令牌响应的 HttpMessageConverter 。可以使用自定义 Converter<Map<String, Object>, OAuth2AccessTokenResponse>OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() 提供,用于将 OAuth 2.0 访问令牌响应参数转换为 OAuth2AccessTokenResponse

OAuth2AccessTokenResponseHttpMessageConverter is an HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() with a custom Converter<Map<String, Object>, OAuth2AccessTokenResponse> that is used for converting the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandlerResponseErrorHandler,它可以处理 OAuth 2.0 错误,如 400 Bad Request。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error.

无论你自定义 DefaultAuthorizationCodeTokenResponseClient 还是提供你自己的 OAuth2AccessTokenResponseClient 实施,都需要按如下方式进行配置:

Whether you customize DefaultAuthorizationCodeTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you need to configure it as follows:

Access Token Response Configuration
  • Java

  • Kotlin

  • Xml

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client(oauth2 -> oauth2
				.authorizationCodeGrant(codeGrant -> codeGrant
					.accessTokenResponseClient(this.accessTokenResponseClient())
					...
				)
			);
		return http.build();
	}
}
@Configuration
@EnableWebSecurity
class OAuth2ClientSecurityConfig {

    @Bean
    open fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http {
            oauth2Client {
                authorizationCodeGrant {
                    accessTokenResponseClient = accessTokenResponseClient()
                }
            }
        }
        return http.build()
    }
}
<http>
	<oauth2-client>
		<authorization-code-grant access-token-response-client-ref="accessTokenResponseClient"/>
	</oauth2-client>
</http>

Refresh Token

有关 Refresh Token 的更多详情,请参阅 OAuth 2.0 授权框架。

See the OAuth 2.0 Authorization Framework for further details on the Refresh Token.

Refreshing an Access Token

有关刷新令牌授权,请参阅 Access Token Request/Response 协议流程。

See the Access Token Request/Response protocol flow for the Refresh Token grant.

OAuth2AccessTokenResponseClient 中刷新令牌授权的默认实施为 DefaultRefreshTokenTokenResponseClient,它在授权服务器令牌端点刷新访问令牌时使用 RestOperations

The default implementation of OAuth2AccessTokenResponseClient for the Refresh Token grant is DefaultRefreshTokenTokenResponseClient, which uses a RestOperations when refreshing an access token at the Authorization Server’s Token Endpoint.

DefaultRefreshTokenTokenResponseClient 十分灵活,因为它允许自定义令牌请求预处理或令牌响应后处理。

The DefaultRefreshTokenTokenResponseClient is flexible, as it lets you customize the pre-processing of the Token Request or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要为令牌请求自定义预处理,则可以向 DefaultRefreshTokenTokenResponseClient.setRequestEntityConverter() 提供自定义 Converter<OAuth2RefreshTokenGrantRequest, RequestEntity<?>> 。默认实现 (OAuth2RefreshTokenGrantRequestEntityConverter) 会构建标准 OAuth 2.0 Access Token RequestRequestEntity 表示。但是,提供自定义 Converter 可以让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide DefaultRefreshTokenTokenResponseClient.setRequestEntityConverter() with a custom Converter<OAuth2RefreshTokenGrantRequest, RequestEntity<?>>. The default implementation (OAuth2RefreshTokenGrantRequestEntityConverter) builds a RequestEntity representation of a standard OAuth 2.0 Access Token Request. However, providing a custom Converter would let you extend the standard Token Request and add custom parameter(s).

要仅自定义请求的参数,可以使用自定义 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>OAuth2RefreshTokenGrantRequestEntityConverter.setParametersConverter() 提供,以完全覆盖随请求发送的参数。这通常比直接构建 RequestEntity 更简单。

To customize only the parameters of the request, you can provide OAuth2RefreshTokenGrantRequestEntityConverter.setParametersConverter() with a custom Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

如果你更愿意只添加其他参数,可以使用自定义 Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>>OAuth2RefreshTokenGrantRequestEntityConverter.addParametersConverter() 提供以构建聚合 Converter

If you prefer to only add additional parameters, you can provide OAuth2RefreshTokenGrantRequestEntityConverter.addParametersConverter() with a custom Converter<OAuth2RefreshTokenGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回 OAuth 2.0 访问令牌请求的有效 RequestEntity 表示,该访问令牌请求由目标 OAuth 2.0 提供商理解。

The custom Converter must return a valid RequestEntity representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果你需要自定义令牌响应后处理,则需要使用自定义配置的 RestOperationsDefaultRefreshTokenTokenResponseClient.setRestOperations() 提供。默认 RestOperations 配置如下:

On the other end, if you need to customize the post-handling of the Token Response, you need to provide DefaultRefreshTokenTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

需要 Spring MVC FormHttpMessageConverter ,因为在发送 OAuth 2.0 访问令牌请求时会使用它。

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter 是 OAuth 2.0 访问令牌响应的 HttpMessageConverter 。可以使用自定义 Converter<Map<String, Object>, OAuth2AccessTokenResponse>OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() 提供,用于将 OAuth 2.0 访问令牌响应参数转换为 OAuth2AccessTokenResponse

OAuth2AccessTokenResponseHttpMessageConverter is a HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() with a custom Converter<Map<String, Object>, OAuth2AccessTokenResponse> that is used for converting the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandlerResponseErrorHandler,它可以处理 OAuth 2.0 错误,如 400 Bad Request。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error.

无论你自定义 DefaultRefreshTokenTokenResponseClient 还是提供你自己的 OAuth2AccessTokenResponseClient 实施,都需要按如下方式进行配置:

Whether you customize DefaultRefreshTokenTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you need to configure it as follows:

  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> refreshTokenTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.authorizationCode()
				.refreshToken(configurer -> configurer.accessTokenResponseClient(refreshTokenTokenResponseClient))
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val refreshTokenTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2RefreshTokenGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .authorizationCode()
        .refreshToken { it.accessTokenResponseClient(refreshTokenTokenResponseClient) }
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().refreshToken() 会配置一个 RefreshTokenOAuth2AuthorizedClientProvider,它是 OAuth2AuthorizedClientProvider 的一种实现,适用于 Refresh Token 授权。

OAuth2AuthorizedClientProviderBuilder.builder().refreshToken() configures a RefreshTokenOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Refresh Token grant.

对于 authorization_codepassword 授权类型,选项为在 Access Token 响应中返回 OAuth2RefreshToken。如果 OAuth2AuthorizedClient.getRefreshToken() 可用,且 OAuth2AuthorizedClient.getAccessToken() 已过期,则 RefreshTokenOAuth2AuthorizedClientProvider 会自动刷新它。

The OAuth2RefreshToken can optionally be returned in the Access Token Response for the authorization_code and password grant types. If the OAuth2AuthorizedClient.getRefreshToken() is available and the OAuth2AuthorizedClient.getAccessToken() is expired, it is automatically refreshed by the RefreshTokenOAuth2AuthorizedClientProvider.

Client Credentials

请参阅 OAuth 2.0 授权框架,进一步了解 Client Credentials 授权。

Please refer to the OAuth 2.0 Authorization Framework for further details on the Client Credentials grant.

Requesting an Access Token

请参阅 OAuth 2.0 授权框架以详细了解 Client Credentials 授权。

See the OAuth 2.0 Authorization Framework for further details on the Client Credentials grant.

OAuth2AccessTokenResponseClient 的默认实现是 Client Credentials 授权的 DefaultClientCredentialsTokenResponseClient,它在 Authorization Server 的 Token 终端请求访问令牌时使用 RestOperations

The default implementation of OAuth2AccessTokenResponseClient for the Client Credentials grant is DefaultClientCredentialsTokenResponseClient, which uses a RestOperations when requesting an access token at the Authorization Server’s Token Endpoint.

DefaultClientCredentialsTokenResponseClient 是灵活的,因为它允许您自定义令牌请求的预处理或令牌响应的后处理。

The DefaultClientCredentialsTokenResponseClient is flexible, as it lets you customize the pre-processing of the Token Request or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要为令牌请求自定义预处理,则可以向 DefaultClientCredentialsTokenResponseClient.setRequestEntityConverter() 提供自定义 Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>> 。默认实现 (OAuth2ClientCredentialsGrantRequestEntityConverter) 会构建标准 OAuth 2.0 Access Token RequestRequestEntity 表示。但是,提供自定义 Converter 可以让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide DefaultClientCredentialsTokenResponseClient.setRequestEntityConverter() with a custom Converter<OAuth2ClientCredentialsGrantRequest, RequestEntity<?>>. The default implementation (OAuth2ClientCredentialsGrantRequestEntityConverter) builds a RequestEntity representation of a standard OAuth 2.0 Access Token Request. However, providing a custom Converter would let you extend the standard Token Request and add custom parameter(s).

要仅自定义请求的参数,您可以为 OAuth2ClientCredentialsGrantRequestEntityConverter.setParametersConverter() 提供一个自定义的 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>,以完全覆盖随请求发送的参数。这通常比直接构建 RequestEntity 更加容易。

To customize only the parameters of the request, you can provide OAuth2ClientCredentialsGrantRequestEntityConverter.setParametersConverter() with a custom Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

如果您只想添加其他的参数,您可以为 OAuth2ClientCredentialsGrantRequestEntityConverter.addParametersConverter() 提供一个自定义的 Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>>,用以构建一个总体的 Converter

If you prefer to only add additional parameters, you can provide OAuth2ClientCredentialsGrantRequestEntityConverter.addParametersConverter() with a custom Converter<OAuth2ClientCredentialsGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回 OAuth 2.0 访问令牌请求的有效 RequestEntity 表示,该访问令牌请求由目标 OAuth 2.0 提供商理解。

The custom Converter must return a valid RequestEntity representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果您需要自定义令牌响应的后处理,则需要为 DefaultClientCredentialsTokenResponseClient.setRestOperations() 提供一个自定义配置的 RestOperations。默认 RestOperations 配置如下:

On the other end, if you need to customize the post-handling of the Token Response, you need to provide DefaultClientCredentialsTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

需要 Spring MVC FormHttpMessageConverter ,因为在发送 OAuth 2.0 访问令牌请求时会使用它。

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter 是 OAuth 2.0 访问令牌响应的 HttpMessageConverter 。可以使用自定义 Converter<Map<String, Object>, OAuth2AccessTokenResponse>OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() 提供,用于将 OAuth 2.0 访问令牌响应参数转换为 OAuth2AccessTokenResponse

OAuth2AccessTokenResponseHttpMessageConverter is a HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() with a custom Converter<Map<String, Object>, OAuth2AccessTokenResponse> that is used for converting the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandler 是一个 ResponseErrorHandler,它可以处理 OAuth 2.0 错误,如 400 Bad Request。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter to convert the OAuth 2.0 Error parameters to an OAuth2Error.

无论您是自定义 DefaultClientCredentialsTokenResponseClient 还是提供您自己的 OAuth2AccessTokenResponseClient 实现方法,都需要按照如下所述配置它:

Whether you customize DefaultClientCredentialsTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you need to configure it as follows:

  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> clientCredentialsTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.clientCredentials(configurer -> configurer.accessTokenResponseClient(clientCredentialsTokenResponseClient))
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val clientCredentialsTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2ClientCredentialsGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .clientCredentials { it.accessTokenResponseClient(clientCredentialsTokenResponseClient) }
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() 会配置一个 ClientCredentialsOAuth2AuthorizedClientProvider,它是 OAuth2AuthorizedClientProvider 的一种实现,适用于 Client Credentials 授权。

OAuth2AuthorizedClientProviderBuilder.builder().clientCredentials() configures a ClientCredentialsOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Client Credentials grant.

Using the Access Token

考虑 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Consider the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: client_credentials
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

进一步考虑以下 OAuth2AuthorizedClientManager @Bean

Further consider the following OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.clientCredentials()
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

鉴于前面的属性和 bean,您可以按照如下方式获取 OAuth2AccessToken

Given the preceding properties and bean, you can obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public String index(Authentication authentication,
						HttpServletRequest servletRequest,
						HttpServletResponse servletResponse) {

		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attributes(attrs -> {
					attrs.put(HttpServletRequest.class.getName(), servletRequest);
					attrs.put(HttpServletResponse.class.getName(), servletResponse);
				})
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		...

		return "index";
	}
}
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication?,
              servletRequest: HttpServletRequest,
              servletResponse: HttpServletResponse): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(Consumer { attrs: MutableMap<String, Any> ->
                    attrs[HttpServletRequest::class.java.name] = servletRequest
                    attrs[HttpServletResponse::class.java.name] = servletResponse
                })
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        ...

        return "index"
    }
}

HttpServletRequestHttpServletResponse 都是可选属性。如果您未指定它们,它们会使用 RequestContextHolder.getRequestAttributes() 默认使用 ServletRequestAttributes

HttpServletRequest and HttpServletResponse are both OPTIONAL attributes. If not provided, they default to ServletRequestAttributes by using RequestContextHolder.getRequestAttributes().

Resource Owner Password Credentials

请参阅 OAuth 2.0 授权框架以详细了解 Resource Owner Password Credentials 授权。

See the OAuth 2.0 Authorization Framework for further details on the Resource Owner Password Credentials grant.

Requesting an Access Token

请参阅 Access Token Request/Response 协议流程以了解资源所有者密码凭据授权。

See the Access Token Request/Response protocol flow for the Resource Owner Password Credentials grant.

OAuth2AccessTokenResponseClient 的默认实现是资源所有者密码凭证授权的 DefaultPasswordTokenResponseClient,它在 Authorization Server 的 Token 终端请求访问令牌时使用 RestOperations

The default implementation of OAuth2AccessTokenResponseClient for the Resource Owner Password Credentials grant is DefaultPasswordTokenResponseClient, which uses a RestOperations when requesting an access token at the Authorization Server’s Token Endpoint.

DefaultPasswordTokenResponseClient 是灵活的,因为它允许您自定义令牌请求的预处理或令牌响应的后处理。

The DefaultPasswordTokenResponseClient is flexible, as it lets you customize the pre-processing of the Token Request or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要为令牌请求自定义预处理,则可以向 DefaultPasswordTokenResponseClient.setRequestEntityConverter() 提供自定义 Converter<OAuth2PasswordGrantRequest, RequestEntity<?>> 。默认实现 (OAuth2PasswordGrantRequestEntityConverter) 会构建标准 OAuth 2.0 Access Token RequestRequestEntity 表示。但是,提供自定义 Converter 可以让您扩展标准令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide DefaultPasswordTokenResponseClient.setRequestEntityConverter() with a custom Converter<OAuth2PasswordGrantRequest, RequestEntity<?>>. The default implementation (OAuth2PasswordGrantRequestEntityConverter) builds a RequestEntity representation of a standard OAuth 2.0 Access Token Request. However, providing a custom Converter would let you extend the standard Token Request and add custom parameter(s).

要仅自定义请求的参数,可为 OAuth2PasswordGrantRequestEntityConverter.setParametersConverter() 提供自定义 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> 以完全覆盖使用该请求发送的参数。这通常比直接构建 RequestEntity 更加简单。

To customize only the parameters of the request, you can provide OAuth2PasswordGrantRequestEntityConverter.setParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

如果你只添加附加参数,可为 OAuth2PasswordGrantRequestEntityConverter.addParametersConverter() 提供自定义 Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>>,它会构建一个聚合 Converter

If you prefer to only add additional parameters, you can provide OAuth2PasswordGrantRequestEntityConverter.addParametersConverter() with a custom Converter<OAuth2PasswordGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

自定义 Converter 必须返回 OAuth 2.0 访问令牌请求的有效 RequestEntity 表示,该访问令牌请求由目标 OAuth 2.0 提供商理解。

The custom Converter must return a valid RequestEntity representation of an OAuth 2.0 Access Token Request that is understood by the intended OAuth 2.0 Provider.

Customizing the Access Token Response

另一方面,如果你需要自定义令牌响应的后处理,需要使用自定义配置的 RestOperationsDefaultPasswordTokenResponseClient.setRestOperations() 提供。默认 RestOperations 的配置如下:

On the other end, if you need to customize the post-handling of the Token Response, you need to provide DefaultPasswordTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

需要 Spring MVC FormHttpMessageConverter ,因为在发送 OAuth 2.0 访问令牌请求时会使用它。

Spring MVC FormHttpMessageConverter is required, as it is used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter 是 OAuth 2.0 访问令牌响应的 HttpMessageConverter。可为 OAuth2AccessTokenResponseHttpMessageConverter.setTokenResponseConverter() 提供自定义 Converter<Map<String, String>, OAuth2AccessTokenResponse>,它用于将 OAuth 2.0 访问令牌响应参数转换为 OAuth2AccessTokenResponse

OAuth2AccessTokenResponseHttpMessageConverter is a HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setTokenResponseConverter() with a custom Converter<Map<String, String>, OAuth2AccessTokenResponse> that is used to convert the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandler 是一个 ResponseErrorHandler,它可以处理 OAuth 2.0 错误,如 400 Bad Request。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, such as 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter to convert the OAuth 2.0 Error parameters to an OAuth2Error.

无论你自定义 DefaultPasswordTokenResponseClient 或提供 OAuth2AccessTokenResponseClient 自己的实现,都需要按照以下方式进行配置:

Whether you customize DefaultPasswordTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you need to configure it as follows:

  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> passwordTokenResponseClient = ...

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.password(configurer -> configurer.accessTokenResponseClient(passwordTokenResponseClient))
				.refreshToken()
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
val passwordTokenResponseClient: OAuth2AccessTokenResponseClient<OAuth2PasswordGrantRequest> = ...

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .password { it.accessTokenResponseClient(passwordTokenResponseClient) }
        .refreshToken()
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

OAuth2AuthorizedClientProviderBuilder.builder().password() 配置 PasswordOAuth2AuthorizedClientProvider,它是资源所有者密码凭据授权的 OAuth2AuthorizedClientProvider 实现。

OAuth2AuthorizedClientProviderBuilder.builder().password() configures a PasswordOAuth2AuthorizedClientProvider, which is an implementation of an OAuth2AuthorizedClientProvider for the Resource Owner Password Credentials grant.

Using the Access Token

考虑 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Consider the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: password
            scope: read, write
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

此外,还需要考虑 OAuth2AuthorizedClientManager @Bean

Further consider the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.password()
					.refreshToken()
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	// Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
	// map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
	authorizedClientManager.setContextAttributesMapper(contextAttributesMapper());

	return authorizedClientManager;
}

private Function<OAuth2AuthorizeRequest, Map<String, Object>> contextAttributesMapper() {
	return authorizeRequest -> {
		Map<String, Object> contextAttributes = Collections.emptyMap();
		HttpServletRequest servletRequest = authorizeRequest.getAttribute(HttpServletRequest.class.getName());
		String username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME);
		String password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD);
		if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
			contextAttributes = new HashMap<>();

			// `PasswordOAuth2AuthorizedClientProvider` requires both attributes
			contextAttributes.put(OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME, username);
			contextAttributes.put(OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME, password);
		}
		return contextAttributes;
	};
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .password()
            .refreshToken()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

    // Assuming the `username` and `password` are supplied as `HttpServletRequest` parameters,
    // map the `HttpServletRequest` parameters to `OAuth2AuthorizationContext.getAttributes()`
    authorizedClientManager.setContextAttributesMapper(contextAttributesMapper())
    return authorizedClientManager
}

private fun contextAttributesMapper(): Function<OAuth2AuthorizeRequest, MutableMap<String, Any>> {
    return Function { authorizeRequest ->
        var contextAttributes: MutableMap<String, Any> = mutableMapOf()
        val servletRequest: HttpServletRequest = authorizeRequest.getAttribute(HttpServletRequest::class.java.name)
        val username = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
        val password = servletRequest.getParameter(OAuth2ParameterNames.PASSWORD)
        if (StringUtils.hasText(username) && StringUtils.hasText(password)) {
            contextAttributes = hashMapOf()

            // `PasswordOAuth2AuthorizedClientProvider` requires both attributes
            contextAttributes[OAuth2AuthorizationContext.USERNAME_ATTRIBUTE_NAME] = username
            contextAttributes[OAuth2AuthorizationContext.PASSWORD_ATTRIBUTE_NAME] = password
        }
        contextAttributes
    }
}

鉴于前面的属性和 bean,您可以按照如下方式获取 OAuth2AccessToken

Given the preceding properties and bean, you can obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/")
	public String index(Authentication authentication,
						HttpServletRequest servletRequest,
						HttpServletResponse servletResponse) {

		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(authentication)
				.attributes(attrs -> {
					attrs.put(HttpServletRequest.class.getName(), servletRequest);
					attrs.put(HttpServletResponse.class.getName(), servletResponse);
				})
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);

		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		...

		return "index";
	}
}
@Controller
class OAuth2ClientController {
    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/")
    fun index(authentication: Authentication?,
              servletRequest: HttpServletRequest,
              servletResponse: HttpServletResponse): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(authentication)
                .attributes(Consumer {
                    it[HttpServletRequest::class.java.name] = servletRequest
                    it[HttpServletResponse::class.java.name] = servletResponse
                })
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        ...

        return "index"
    }
}

HttpServletRequestHttpServletResponse 均为可选属性。如果未提供,它们将使用 RequestContextHolder.getRequestAttributes() 默认 ServletRequestAttributes

HttpServletRequest and HttpServletResponse are both OPTIONAL attributes. If not provided, they default to ServletRequestAttributes using RequestContextHolder.getRequestAttributes().

JWT Bearer

请参阅 JSON Web Token (JWT) OAuth 2.0 客户端认证和认证授予简介以详细了解 JWT Bearer 授权。

Please refer to JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and Authorization Grants for further details on the JWT Bearer grant.

Requesting an Access Token

请参阅 Access Token Request/Response 协议流程以了解 JWT 持有者授权。

Please refer to the Access Token Request/Response protocol flow for the JWT Bearer grant.

OAuth2AccessTokenResponseClient 用于 JWT 持有者的默认实现为 DefaultJwtBearerTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 RestOperations

The default implementation of OAuth2AccessTokenResponseClient for the JWT Bearer grant is DefaultJwtBearerTokenResponseClient, which uses a RestOperations when requesting an access token at the Authorization Server’s Token Endpoint.

DefaultJwtBearerTokenResponseClient 非常灵活,因为它允许你自定义令牌请求的预处理和/或令牌响应的后处理。

The DefaultJwtBearerTokenResponseClient is quite flexible as it allows you to customize the pre-processing of the Token Request and/or post-handling of the Token Response.

Customizing the Access Token Request

如果您需要为令牌请求自定义预处理,则可以向 DefaultJwtBearerTokenResponseClient.setRequestEntityConverter() 提供自定义 Converter<JwtBearerGrantRequest, RequestEntity<?>> 。默认实现 JwtBearerGrantRequestEntityConverter 会构建 OAuth 2.0 Access Token RequestRequestEntity 表示。但是,提供自定义 Converter 可以让您扩展令牌请求并添加自定义参数。

If you need to customize the pre-processing of the Token Request, you can provide DefaultJwtBearerTokenResponseClient.setRequestEntityConverter() with a custom Converter<JwtBearerGrantRequest, RequestEntity<?>>. The default implementation JwtBearerGrantRequestEntityConverter builds a RequestEntity representation of a OAuth 2.0 Access Token Request. However, providing a custom Converter, would allow you to extend the Token Request and add custom parameter(s).

要仅自定义请求的参数,可为 JwtBearerGrantRequestEntityConverter.setParametersConverter() 提供自定义 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> 以完全覆盖使用该请求发送的参数。这通常比直接构建 RequestEntity 更加简单。

To customize only the parameters of the request, you can provide JwtBearerGrantRequestEntityConverter.setParametersConverter() with a custom Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> to completely override the parameters sent with the request. This is often simpler than constructing a RequestEntity directly.

如果您愿意仅添加其他参数,可以为 JwtBearerGrantRequestEntityConverter.addParametersConverter() 提供自定义 Converter<JwtBearerGrantRequest, MultiValueMap<String, String>>,由其构建一个聚合 Converter

If you prefer to only add additional parameters, you can provide JwtBearerGrantRequestEntityConverter.addParametersConverter() with a custom Converter<JwtBearerGrantRequest, MultiValueMap<String, String>> which constructs an aggregate Converter.

Customizing the Access Token Response

另一方面,如果你需要自定义令牌响应的后处理,将需要使用自定义配置的 RestOperationsDefaultJwtBearerTokenResponseClient.setRestOperations() 提供。默认 RestOperations 的配置如下:

On the other end, if you need to customize the post-handling of the Token Response, you will need to provide DefaultJwtBearerTokenResponseClient.setRestOperations() with a custom configured RestOperations. The default RestOperations is configured as follows:

  • Java

  • Kotlin

RestTemplate restTemplate = new RestTemplate(Arrays.asList(
		new FormHttpMessageConverter(),
		new OAuth2AccessTokenResponseHttpMessageConverter()));

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());
val restTemplate = RestTemplate(listOf(
        FormHttpMessageConverter(),
        OAuth2AccessTokenResponseHttpMessageConverter()))

restTemplate.errorHandler = OAuth2ErrorResponseErrorHandler()

需要 Spring MVC FormHttpMessageConverter,因为在发送 OAuth 2.0 访问令牌请求时会使用它。

Spring MVC FormHttpMessageConverter is required as it’s used when sending the OAuth 2.0 Access Token Request.

OAuth2AccessTokenResponseHttpMessageConverter 是 OAuth 2.0 访问令牌响应的 HttpMessageConverter 。可以使用自定义 Converter<Map<String, Object>, OAuth2AccessTokenResponse>OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() 提供,用于将 OAuth 2.0 访问令牌响应参数转换为 OAuth2AccessTokenResponse

OAuth2AccessTokenResponseHttpMessageConverter is a HttpMessageConverter for an OAuth 2.0 Access Token Response. You can provide OAuth2AccessTokenResponseHttpMessageConverter.setAccessTokenResponseConverter() with a custom Converter<Map<String, Object>, OAuth2AccessTokenResponse> that is used for converting the OAuth 2.0 Access Token Response parameters to an OAuth2AccessTokenResponse.

OAuth2ErrorResponseErrorHandler 是一个 ResponseErrorHandler,它可以处理 OAuth 2.0 错误,例如 400 Bad Request。它使用 OAuth2ErrorHttpMessageConverter 将 OAuth 2.0 错误参数转换为 OAuth2Error

OAuth2ErrorResponseErrorHandler is a ResponseErrorHandler that can handle an OAuth 2.0 Error, eg. 400 Bad Request. It uses an OAuth2ErrorHttpMessageConverter for converting the OAuth 2.0 Error parameters to an OAuth2Error.

无论你自定义 DefaultJwtBearerTokenResponseClient 或提供 OAuth2AccessTokenResponseClient 自己的实现,都需要按照以下示例所示对其进行配置:

Whether you customize DefaultJwtBearerTokenResponseClient or provide your own implementation of OAuth2AccessTokenResponseClient, you’ll need to configure it as shown in the following example:

  • Java

  • Kotlin

// Customize
OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> jwtBearerTokenResponseClient = ...

JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider = new JwtBearerOAuth2AuthorizedClientProvider();
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);

OAuth2AuthorizedClientProvider authorizedClientProvider =
		OAuth2AuthorizedClientProviderBuilder.builder()
				.provider(jwtBearerAuthorizedClientProvider)
				.build();

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);
// Customize
val jwtBearerTokenResponseClient: OAuth2AccessTokenResponseClient<JwtBearerGrantRequest> = ...

val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
jwtBearerAuthorizedClientProvider.setAccessTokenResponseClient(jwtBearerTokenResponseClient);

val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
        .provider(jwtBearerAuthorizedClientProvider)
        .build()

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)

Using the Access Token

给定 OAuth 2.0 客户端注册的以下 Spring Boot 2.x 属性:

Given the following Spring Boot 2.x properties for an OAuth 2.0 Client registration:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            authorization-grant-type: urn:ietf:params:oauth:grant-type:jwt-bearer
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

…​和 OAuth2AuthorizedClientManager @Bean

…​and the OAuth2AuthorizedClientManager @Bean:

  • Java

  • Kotlin

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientRepository authorizedClientRepository) {

	JwtBearerOAuth2AuthorizedClientProvider jwtBearerAuthorizedClientProvider =
			new JwtBearerOAuth2AuthorizedClientProvider();

	OAuth2AuthorizedClientProvider authorizedClientProvider =
			OAuth2AuthorizedClientProviderBuilder.builder()
					.provider(jwtBearerAuthorizedClientProvider)
					.build();

	DefaultOAuth2AuthorizedClientManager authorizedClientManager =
			new DefaultOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientRepository);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val jwtBearerAuthorizedClientProvider = JwtBearerOAuth2AuthorizedClientProvider()
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .provider(jwtBearerAuthorizedClientProvider)
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

可按如下方式获取 OAuth2AccessToken

You may obtain the OAuth2AccessToken as follows:

  • Java

  • Kotlin

@RestController
public class OAuth2ResourceServerController {

	@Autowired
	private OAuth2AuthorizedClientManager authorizedClientManager;

	@GetMapping("/resource")
	public String resource(JwtAuthenticationToken jwtAuthentication) {
		OAuth2AuthorizeRequest authorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
				.principal(jwtAuthentication)
				.build();
		OAuth2AuthorizedClient authorizedClient = this.authorizedClientManager.authorize(authorizeRequest);
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		...

	}
}
class OAuth2ResourceServerController {

    @Autowired
    private lateinit var authorizedClientManager: OAuth2AuthorizedClientManager

    @GetMapping("/resource")
    fun resource(jwtAuthentication: JwtAuthenticationToken?): String {
        val authorizeRequest: OAuth2AuthorizeRequest = OAuth2AuthorizeRequest.withClientRegistrationId("okta")
                .principal(jwtAuthentication)
                .build()
        val authorizedClient = authorizedClientManager.authorize(authorizeRequest)
        val accessToken: OAuth2AccessToken = authorizedClient.accessToken

        ...

    }
}

JwtBearerOAuth2AuthorizedClientProvider 默认通过 OAuth2AuthorizationContext.getPrincipal().getPrincipal() 解析 Jwt 断言,因此在前面的示例中使用了 JwtAuthenticationToken

JwtBearerOAuth2AuthorizedClientProvider resolves the Jwt assertion via OAuth2AuthorizationContext.getPrincipal().getPrincipal() by default, hence the use of JwtAuthenticationToken in the preceding example.

如果您需要从不同来源解析 Jwt 断言,可以为 JwtBearerOAuth2AuthorizedClientProvider.setJwtAssertionResolver() 提供自定义 Function<OAuth2AuthorizationContext, Jwt>

If you need to resolve the Jwt assertion from a different source, you can provide JwtBearerOAuth2AuthorizedClientProvider.setJwtAssertionResolver() with a custom Function<OAuth2AuthorizationContext, Jwt>.