SpringSecurity中文文档(Servlet OAuth 2.0 Client)

OAuth 2.0 Client

OAuth 2.0 Client 特性提供了对 OAuth 2.0授权框架中定义的 Client 角色的支持。

在高层次,可用的核心特征是:

Authorization Grant support

Client Authentication support

HTTP Client support

  • 用于 Servlet 环境的 WebClient 集成(用于请求受保护的资源)

Oauth2Client () DSL 为定制 OAuth 2.0 Client 使用的核心组件提供了许多配置选项。另外,HttpSecurity.oauth2Client().authorizationCodeGrant()启用授权代码授权的自定义。

下面的代码显示了 HttpSecurity.oauth2Client () DSL 提供的完整配置选项:

OAuth2 Client Configuration Options

@Configuration
@EnableWebSecurity
public class OAuth2ClientSecurityConfig {

	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		http
			.oauth2Client(oauth2 -> oauth2
				.clientRegistrationRepository(this.clientRegistrationRepository())
				.authorizedClientRepository(this.authorizedClientRepository())
				.authorizedClientService(this.authorizedClientService())
				.authorizationCodeGrant(codeGrant -> codeGrant
					.authorizationRequestRepository(this.authorizationRequestRepository())
					.authorizationRequestResolver(this.authorizationRequestResolver())
					.accessTokenResponseClient(this.accessTokenResponseClient())
				)
			);
		return http.build();
	}
}

除了 HttpSecurity.oauth2Client () DSL 之外,还支持 XML 配置。

下面的代码显示了security namespace中可用的完整配置选项:

OAuth2 Client XML Configuration Options

<http>
	<oauth2-client client-registration-repository-ref="clientRegistrationRepository"
				   authorized-client-repository-ref="authorizedClientRepository"
				   authorized-client-service-ref="authorizedClientService">
		<authorization-code-grant
				authorization-request-repository-ref="authorizationRequestRepository"
				authorization-request-resolver-ref="authorizationRequestResolver"
				access-token-response-client-ref="accessTokenResponseClient"/>
	</oauth2-client>
</http>

OAuth2AuthorizedClientManager 与一个或多个 OAuth2AuthorizedClientProvider 协作,负责管理 OAuth 2.0客户端的授权(或重新授权)。

下面的代码显示了如何注册一个 OAuth2AuthorizedClientManager@Bean 并将其与一个 OAuth2AuthorizedClientProvider 组合关联的示例,该组合提供uthorization_code``, ``refresh_token``, ``client_credentials``, and ``password授予类型的支持:

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

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

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

	return authorizedClientManager;
}

Section Summary

Core Interfaces and Classes

本节介绍 SpringSecurity 提供的 OAuth2核心接口和类。

ClientRegistration

ClientRegistry 是用 OAuth 2.0或 OpenID Connect 1.0 Provider 注册的客户机的表示形式。

ClientRegistry 对象保存信息,如客户机 ID、客户机机密、授权授予类型、重定向 URI、作用域、授权 URI、令牌 URI 和其他详细信息。

ClientRegistry 及其属性定义如下:

public final class ClientRegistration {
	private String registrationId;	 //1
	private String clientId;	 //2
	private String clientSecret;	//3
	private ClientAuthenticationMethod clientAuthenticationMethod;	//4
	private AuthorizationGrantType authorizationGrantType;	//5
	private String redirectUri;	//6
	private Set<String> scopes;	//7
	private ProviderDetails providerDetails;
	private String clientName;	//8

	public class ProviderDetails {
		private String authorizationUri;	//9
		private String tokenUri;	//10
		private UserInfoEndpoint userInfoEndpoint;
		private String jwkSetUri;	//11
		private String issuerUri;	//12
        private Map<String, Object> configurationMetadata;//13

		public class UserInfoEndpoint {
			private String uri;	//14
            private AuthenticationMethod authenticationMethod;//15
			private String userNameAttributeName;	//16

		}
	}
}
  1. RegistrationId: 唯一标识 ClientRegistration的 ID。
  2. ClientId: 客户端标识符。
  3. clientSecret: 客户端密码。
  4. ClientAuthenticationMethod: 用于通过提供程序对客户端进行身份验证的方法。支持的值是 client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none (public clients).
  5. AuthorizationGrantType: OAuth 2.0授权框架定义了四种授权授权类型。支持的值包括 authorization_code, client_credentials, password 以及扩展授权类型 urn:ietf:params:oauth:grant-type:jwt-bearer。
  6. RedirectUri: 在最终用户对客户端进行身份验证和授权访问之后,Authorization Server 将最终用户的用户代理重定向到的客户端注册的重定向 URI。
  7. scopes: 客户端在授权请求流(如 openid、电子邮件或配置文件)中请求的作用域。
  8. ClientName: 用于客户端的描述性名称。该名称可用于某些场景,例如在自动生成的登录页面中显示客户端名称时。
  9. AuthorizationUri: 授权服务器的授权端点 URI。
  10. TokenUri: 授权服务器的令牌端点 URI。
  11. JwkSetUri: 用于从 Authorization Server 检索 JSON Web Key (JWK) Set 的 URI,其中包含用于验证 ID 令牌的 JSON Web Signature (JWS)和(可选) UserInfo Response 的单密钥。
  12. IsserUri: 返回 OpenID Connect 1.0提供程序或 OAuth 2.0 Authorization Server 的发行者标识符 URI。
  13. ConfigationMetadata: OpenID 提供程序配置信息。只有在配置了 Spring Boot 属性 Spring.security.oauth2.client.Provider. [ provision erId ] . isserUri 时,此信息才可用。
  14. (userInfoEndpoint) URI: 用于访问经过身份验证的最终用户的声明和属性的 UserInfo Endpoint URI。
  15. (userInfoEndpoint) enticationMethod: 向 UserInfo 端点发送访问令牌时使用的身份验证方法。支持的值是头、表单和查询。
  16. UserNameAttributeName: 在 UserInfo Response 中返回的引用最终用户的 Name 或 Identifier 的属性的名称。

通过使用 OpenID 连接提供程序的配置端点或授权服务器的元数据端点的发现,最初可以配置 ClientRegistration

ClientRegistration以这种方式为配置 ClientRegistration提供了方便的方法,如下所示:

ClientRegistration clientRegistration =
    ClientRegistrations.fromIssuerLocation("https://idp.example.com/issuer").build();

前面的代码查询按序列、 idp.example.com/issuer/.well-known/openid-configuration、 idp.example.com/.well-known/openid-configuration/issuer 和 idp.example.com/.well-known/oauth-authorization-server/issuer 进行,在第一个查询结束时返回200个响应。

作为替代方案,您可以使用 ClientRegistrations.forOidcIssuerLocation ()仅查询 OpenID 连接提供程序的 Configuration 端点。

ClientRegistrationRepository

ClientRegistrationRepository 充当 OAuth 2.0/OpenID Connect 1.0 ClientRegistration(s)的存储库。

客户端注册信息最终由关联的 AuthorizationServer 存储和拥有。此存储库提供了检索主客户端注册信息子集的能力,这些信息存储在 AuthorizationServer 中。

Spring Boot 自动配置绑定 spring.security.oauth2.client.registration.[registrationId] 的实例,然后在 ClientRegistrationRepository 中组合每个 ClientRegistration实例。

ClientRegistrationRepository 的默认实现是 InmemyClientRegistrationRepository。

自动配置还在 ApplicationContext 中将 ClientregistrationRepository 注册为一个@Bean,以便在应用程序需要的时候,它可以用于依赖注入。

下面的清单显示了一个示例:

@Controller
public class OAuth2ClientController {

	@Autowired
	private ClientRegistrationRepository clientRegistrationRepository;

	@GetMapping("/")
	public String index() {
		ClientRegistration oktaRegistration =
			this.clientRegistrationRepository.findByRegistrationId("okta");

		...

		return "index";
	}
}

OAuth2AuthorizedClient

OAuth2AuthorizedClient 是授权客户端的表示形式。当最终用户(资源所有者)授予客户端访问其受保护资源的权限时,客户端被认为是被授权的。

OAuth2AuthorizedClient 用于将 OAuth2AccessToken (和可选的 OAuth2RefreshToken)关联到 ClientRegistry (客户端)和资源所有者,后者是授予授权的主要最终用户。

OAuth2AuthorizedClientRepository and OAuth2AuthorizedClientService

OAuth2AuthorizedClientRepository 负责在 Web 请求之间保存 OAuth2AuthorizedClient (s) ,而 OAuth2AuthorizedClientService 的主要角色是在应用程序级别管理 OAuth2AuthorizedClient (s)。

从开发人员的角度来看,OAuth2AuthorizedClientRepository 或 OAuth2AuthorizedClientService 提供了查找与客户端关联的 OAuth2AccessToken 的能力,以便可以使用它来启动受保护的资源请求。

下面的清单显示了一个示例:

@Controller
public class OAuth2ClientController {

    @Autowired
    private OAuth2AuthorizedClientService authorizedClientService;

    @GetMapping("/")
    public String index(Authentication authentication) {
        OAuth2AuthorizedClient authorizedClient =
            this.authorizedClientService.loadAuthorizedClient("okta", authentication.getName());

        OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

        ...

        return "index";
    }
}

OAuth2AuthorizedClientService 的默认实现是 InMemorial yOAuth2AuthorizedClientService,它在内存中存储 OAuth2AuthorizedClient 对象。

或者,您可以配置 JDBC 实现 JdbcOAuth2AuthorizedClientService 以在数据库中持久存储 OAuth2AuthorizedClient 实例。

JdbcOAuth2AuthorizedClientService 依赖于 OAuth 2.0 Client Schema.。

OAuth2AuthorizedClientManager and OAuth2AuthorizedClientProvider

OAuth2AuthorizedClientManager 负责 OAuth2AuthorizedClient (s)的全面管理。

主要职责包括:

  • 使用 OAuth2AuthorizedClientProvider 授权(或重新授权) OAuth2.0客户端。
  • 委托 OAuth2AuthorizedClient 的持久性,通常使用 OAuth2AuthorizedClientService 或 OAuth2AuthorizedClientRepository。
  • 当 OAuth 2.0客户端已经成功授权(或重新授权)时,委托给 OAuth2AuthorizationSuccess Handler。
  • 当 OAuth 2.0客户端无法授权(或重新授权)时,委托给 OAuth2AuthorizationFallureHandler。

OAuth2AuthorizedClientProvider 实现了授权(或重新授权) OAuth 2.0客户端的策略。实现通常实现授权授予类型,例如 authorization_code, client_credentials, and others.

OAuth2AuthorizedClientManager 的默认实现是 DefaultOAuth2AuthorizedClientManager,它与 OAuth2AuthorizedClientProvider 相关联,OAuth2AuthorizedClientProvider 可以使用基于委托的组合支持多种授权授权类型。可以使用 OAuth2AuthorizedClientProviderBuilder 配置和生成基于委托的组合。

下面的代码显示了如何配置和构建 OAuth2AuthorizedClientProvider 组合的示例,该组合提供对authorization_code, refresh_token, client_credentials, and password授予类型的支持:

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

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

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

	return authorizedClientManager;
}

当授权尝试成功时,DefaultOAuth2AuthorizedClientManager 委托给 OAuth2AuthorizationSuccess Handler,该委托(默认情况下)通过 OAuth2AuthorizedClientRepository 保存 OAuth2AuthorizedClient。在重新授权失败的情况下(例如,刷新令牌不再有效) ,先前保存的 OAuth2AuthorizedClient 将通过 RemoveAuthorizedClientOAuthizationAuthorureHandler 从 OAuth2AuthorizedClientRepository 中删除。您可以通过 setAuthorizationSuccess Handler (OAuth2AuthorizationSuccess Handler)和 setAuthorizationfalureHandler (OAuth2AuthorizationfalureHandler)定制默认行为。

DefaultOAuth2AuthorizedClientManager 还与类型为 Function <OAuth2AuthorizeRequest,Map < String,Object > 的 contextAttributesMapper 相关联,后者负责将属性从 OAuth2AuthorizeRequest 映射到与 OAuth2AuthorizationContext 相关联的属性映射。当您需要为 OAuth2AuthorizedClientProvider 提供必需的(受支持的)属性时,例如 PasswordOAuth2AuthorizedClientProvider 要求资源所有者的用户名和密码在 OAuth2AuthorizationContext.getAttritribute ()中可用时,这会很有用。

下面的代码显示 contextAttributesMapper 的示例:

@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;
	};
}

DefaultOAuth2AuthorizedClientManager 设计用于在 HttpServletRequest 上下文中使用。在 HttpServletRequest 上下文之外操作时,请改为使用 AuthorizedClientServiceOAuth2AuthorizedClientManager。

服务应用程序是何时使用AuthorizedClientServiceOAuth2AuthorizedClientManager 的常见用例。服务应用程序通常在后台运行,没有任何用户交互,通常在系统级帐户而不是用户帐户下运行。配置了client_credentials授予类型的 OAuth 2.0 Client 可以被认为是一种服务应用程序类型。

下面的代码显示如何配置 AuthorizedClientServiceOAuth2AuthorizedClientManager 的示例,该管理器提供对 client_credentials授予类型的支持:

@Bean
public OAuth2AuthorizedClientManager authorizedClientManager(
		ClientRegistrationRepository clientRegistrationRepository,
		OAuth2AuthorizedClientService authorizedClientService) {

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

	AuthorizedClientServiceOAuth2AuthorizedClientManager authorizedClientManager =
			new AuthorizedClientServiceOAuth2AuthorizedClientManager(
					clientRegistrationRepository, authorizedClientService);
	authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

	return authorizedClientManager;
}

Authorization Grant Support

本节描述 SpringSecurity 对授权授予的支持。

Authorization Code

有关 Authorization Code 授予的详细信息,请参阅 OAuth 2.0授权框架。

Obtaining Authorization

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

Initiating the Authorization Request

OAuth2AuthorizationRequestRedirectFilter 使用 OAuth2AuthorizationRequestResolver 解析 OAuth2AuthorizationRequest,并通过将最终用户的用户代理重定向到 AuthorizationServer 的 AuthorizationEndpoint 来启动授权代码授权流。

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

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

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/authority/okta 的请求将通过 OAuth2AuthorizationRequestRedirectFilter 启动 Authorization Request 重定向,并最终启动 Authorization Code 授权流。

AuthorizationCodeOAuth2AuthorizedClientProvider 是用于授权代码授权的 OAuth2AuthorizedClientProvider 的实现,它还通过 OAuth2AuthorizationRequestRedirectFilter 启动授权请求重定向。

如果 OAuth 2.0客户端是一个公共客户端,则按以下方式配置 OAuth 2.0客户端注册:

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)支持公共客户端。如果客户端运行在不受信任的环境(例如本地应用程序或基于 Web 浏览器的应用程序)中,因此无法维护其凭证的机密性,则在符合下列条件时自动使用 PKCE:

  • Client-secret 被省略(或为空)
  • Client-enticationMethod 设置为 none (ClientAuthenticationMethod.NONE)

如果 OAuth 2.0提供程序支持机密客户端的 PKCE,您可以(可选)使用 DefaultOAuth2AuthorizationRequestResolver.setAuthorizationRequestCustomizer (OAuth2AuthorizationRequestCustomizers.withPkce ())配置它。

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

当 OAuth 2.0客户机运行在代理服务器之后时,使用 URI 模板变量配置 redirect-URI 特别有用。这样做可以确保在展开 redirect-uri 时使用 X-Forward-* 头。

Customizing the Authorization Request

OAuth2AuthorizationRequestResolver 可以实现的主要用例之一是使用超出 OAuth 2.0 Authorization Framework 中定义的标准参数的附加参数来定制授权请求的能力。

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

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

下面的示例演示如何使用 Consumer < OAuth2AuthorizationRequest.Builder > 配置 DefaultOAuth2AuthorizationRequestResolver,通过包含请求参数顿 = 同意来自定义 oauth2Login ()的授权请求。

@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"));
	}
}

对于一个简单的用例,其中附加的请求参数对于特定的提供程序始终相同,您可以直接将其添加到 Authorization-uri 属性中。

例如,如果请求参数提示符的值始终是提供程序 okta 的同意,则可以按以下方式配置它:

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

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

OAuth2AuthorizationRequest. Builder.build ()构造 OAuth2AuthorizationRequest.authorizationRequestUri,它表示包含使用 application/x-www-form-urlencode 格式的所有查询参数的 AuthorizationRequest URI。

下面的示例显示前面示例中的 AuthorizationRequestCustomizer ()的变体,并重写 OAuth2AuthorizationRequest.authorizationRequestUri 属性:

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

Storing the Authorization Request

AuthorizationRequestRepository 负责 OAuth2AuthorizationRequest 的持久性,从授权请求开始到接收授权响应(回调)。

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

AuthorizationRequestRepository 的默认实现是 HttpSessionOAuthizationRequestRepository,它在 HttpSession 中存储 OAuth2AuthorizationRequest。

如果您有 AuthorizationRequestRepository 的自定义实现,您可以按照以下方式配置它:

AuthorizationRequestRepository Configuration

@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();
    }
}

Requesting an Access Token

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

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

Customizing the Access Token Request

如果需要自定义令牌请求的预处理,可以为 DefaultAuthorizationCodeTokenResponseClient.setRequestEntityConverter ()提供自定义 Converter < OAuth2AuthorizationCodeGrantRequest,RequestEntity < ?>>.默认实现(OAuth2AuthorizationCodeGrantRequestEntityConverter)构建标准 OAuth 2.0访问令牌请求的 RequestEntity 表示。但是,提供自定义转换器将允许您扩展标准令牌请求并添加自定义参数。

为了只定制请求的参数,您可以为 OAuth2AuthorizationCodeGrantRequestEntityConverter.setParameter tersConverter ()提供自定义 Converter < OAuth2AuthorizationCodeGrantRequest,MultiValueMap < String,String > > 以完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

如果您希望只添加其他参数,可以为 OAuth2AuthorizationCodeGrantRequestEntityConverter.addParameter tersConverter ()提供自定义 Converter < OAuth2AuthorizationCodeGrantRequest,MultiValueMap < String,String > > ,用于构造聚合转换器。

自定义 Converter 必须返回 OAuth 2.0 Access Token Request 的有效 RequestEntity 表示形式,OAuth 2.0提供程序可以理解该表示形式。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultAuthorizationCodeTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,都需要将其配置如下:

Access Token Response Configuration

@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();
	}
}

Refresh Token

有关 Refresh Token的详细信息,请参阅 OAuth 2.0授权框架。

Refreshing an Access Token

有关刷新令牌授予,请参见 Access Token Request/Response协议流。

OAuth2AccessTokenResponseClient 用于刷新令牌授予的默认实现是 DefaultRefreshTokenTokenResponseClient,它在授权服务器的令牌端点刷新访问令牌时使用 RestOperations。

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

Customizing the Access Token Request

如果需要自定义令牌请求的预处理,可以使用自定义 Converter < OAuth2RefreshTokenGrantRequest,RequestEntityConverter ()提供 DefaultRefreshTokenResponseClient.setRequestEntityConverter ()?>>.默认实现(OAuth2RefreshTokenGrantRequestEntityConverter)构建标准 OAuth 2.0访问令牌请求的 RequestEntity 表示。但是,提供自定义转换器将允许您扩展标准令牌请求并添加自定义参数。

为了只定制请求的参数,您可以为 OAuth2RefreshTokenGrantRequestEntityConverter.setParameter tersConverter ()提供一个自定义 Converter < OAuth2RefreshTokenGrantRequest,MultiValueMap < String,String > > 来完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

如果您希望只添加其他参数,可以为 OAuth2RefreshTokenGrantRequestEntityConverter.addParameter tersConverter ()提供一个定制 Converter < OAuth2RefreshTokenGrantRequest,MultiValueMap < String,String > > ,用于构造聚合转换器。

自定义 Converter 必须返回 OAuth 2.0 Access Token Request 的有效 RequestEntity 表示形式,OAuth 2.0提供程序可以理解该表示形式。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultRefreshTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,您都需要将其配置如下:

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

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

OAuth2AuthorizedClientProviderBuilder.Builder () . resh Token ()配置 RefreshTokenOAuth2AuthorizedClientProvider,这是用于刷新令牌授予的 OAuth2AuthorizedClientProvider 的实现。

OAuth2RefreshToken 可以选择在 Access Token Response 中返回 Authorization _ code 类型和密码授予类型。如果 OAuth2AuthorizedClient.getRefreshToken ()可用且 OAuth2AuthorizedClient.getAccessToken ()过期,则 RefreshTokenOAuth2AuthorizedClientProvider 将自动刷新它。

Client Credentials

请参阅 OAuth 2.0授权框架以获得有关 Client Credentials 授予的更多详细信息。

Requesting an Access Token

有关 Client Credentials 授予的详细信息,请参阅 OAuth 2.0授权框架。

OAuth2AccessTokenResponseClient 用于客户端凭据授予的默认实现是 DefaultClientCreentialsTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 RestOperations。

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

Customizing the Access Token Request

如果需要自定义令牌请求的预处理,可以使用自定义 Converter < OAuth2ClientCredenalsGrantRequest,RequestEntityConverter ()提供 DefaultClientCredenalsTokenResponseClient.setRequestEntityConverter ()?>>.默认实现(OAuth2ClientCredenalsGrantRequestEntityConverter)构建标准 OAuth 2.0访问令牌请求的 RequestEntity 表示。但是,提供自定义转换器将允许您扩展标准令牌请求并添加自定义参数。

为了只定制请求的参数,您可以为 OAuth2ClientCredenalsGrantRequestEntityConverter.setParameter tersConverter ()提供一个自定义 Converter < OAuth2ClientCredenalsGrantRequest,MultiValueMap < String,String > > 来完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

如果您希望只添加其他参数,可以为 OAuth2ClientCredenalsGrantRequestEntityConverter.addParameter tersConverter ()提供自定义 Converter < OAuth2ClientCredenalsGrantRequest,MultiValueMap < String,String > > ,用于构造聚合转换器。

自定义 Converter 必须返回 OAuth 2.0 Access Token Request 的有效 RequestEntity 表示形式,OAuth 2.0提供程序可以理解该表示形式。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultClientCreentialsTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,都需要按照以下方式配置它:

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

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

OAuth2AuthorizedClientProviderBuilder.Builder () . clientCredenals ()配置一个 ClientCredenalsOAuth2AuthorizedClientProvider,它是用于客户凭证授予的 OAuth2AuthorizedClientProvider 的实现。

Using the Access Token

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

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:

@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,您可以按如下方式获得 OAuth2AccessToken:

@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";
	}
}

HttpServletRequest 和 HttpServletResponse 都是 OPTION 属性,如果没有提供,它们默认使用 RequestContextHolder.getRequestAttritribute ()。

Resource Owner Password Credentials

有关Resource Owner Password Credentials授予的详细信息,请参阅 OAuth 2.0授权框架。

Requesting an Access Token

有关Resource Owner Password Credentials凭据授予,请参见访问令牌请求/响应协议流。

资源所有者密码凭证授予的 OAuth2AccessTokenResponseClient 的默认实现是 DefaultPasswordTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 RestOperations。

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

Customizing the Access Token Request

如果您需要自定义令牌请求的预处理,可以使用自定义 Converter < OAuth2PasswordGrantRequest,RequestEntityConverter ()提供 DefaultPasswordTokenResponseClient.setRequestEntityConverter ()?>>.默认实现(OAuth2PasswordGrantRequestEntityConverter)构建标准 OAuth 2.0访问令牌请求的 RequestEntity 表示。但是,提供自定义转换器将允许您扩展标准令牌请求并添加自定义参数。

为了只定制请求的参数,您可以为 OAuth2PasswordGrantRequestEntityConverter.setParameter tersConverter ()提供一个自定义 Converter < OAuth2PasswordGrantRequest,MultiValueMap < String,String > > 来完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

如果您希望只添加其他参数,可以为 OAuth2PasswordGrantRequestEntityConverter.addParameter tersConverter ()提供自定义 Converter < OAuth2PasswordGrantRequest,MultiValueMap < String,String > > ,用于构造聚合转换器。

自定义 Converter 必须返回 OAuth 2.0 Access Token Request 的有效 RequestEntity 表示形式,OAuth 2.0提供程序可以理解该表示形式。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultPasswordTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,都需要将其配置如下:

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

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

OAuth2AuthorizedClientProviderBuilder.Builder () . Password ()配置 PasswordOAuth2AuthorizedClientProvider,它是资源所有者密码凭证授予的 OAuth2AuthorizedClientProvider 的实现。

Using the Access Token

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

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:

@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,您可以按如下方式获得 OAuth2AccessToken:

@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";
	}
}

HttpServletRequest 和 HttpServletResponse 都是 OPTION 属性,如果没有提供,它们默认使用 RequestContextHolder.getRequestAttritribute ()设置为 ServletRequestAttritribute。

JWT Bearer

请参考 OAuth 2.0客户端身份验证和授权授权的 JSON Web Token (JWT) Profile,以获得关于 JWT Bearer 授权的更多细节。

Requesting an Access Token

请参考访问令牌请求/响应协议流以获得 JWT Bearer 授权。

OAuth2AccessTokenResponseClient 的默认实现是 DefaultJwtBearerTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 RestOperations。

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

Customizing the Access Token Request

如果需要自定义令牌请求的预处理,可以为 DefaultJwtBearerTokenResponseClient.setRequestEntityConverter ()提供自定义 Converter < JwtBearerGrantRequest,RequestEntity < ?>>.默认实现 JwtBearerGrantRequestEntityConverter 构建 OAuth 2.0访问令牌请求的 RequestEntity 表示。但是,提供自定义转换器将允许您扩展令牌请求并添加自定义参数。

为了只定制请求的参数,您可以为 JwtBearerGrantRequestEntityConverter.setParameter tersConverter ()提供一个自定义 Converter < JwtBearerGrantRequest,MultiValueMap < String,String > > 来完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

如果您希望只添加其他参数,可以为 JwtBearerGrantRequestEntityConverter.addParameter tersConverter ()提供定制 Converter < JwtBearerGrantRequest,MultiValueMap < String,String > > ,它构造一个聚合 Converter。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultJwtBearerTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,都需要按照下面的示例进行配置:

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

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

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

Using the Access Token

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

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:

@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;
}

你可按以下方式取得 OAuth2AccessToken:

@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();

		...

	}
}

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

如果需要从不同的源解析 Jwt 断言,可以为 JwtBearerOAuthizedClientProvider.setJwtAssertionResolver ()提供自定义函数 < OAuth2AuthorizationContext,Jwt > 。

Token Exchange

有关 Token Exchange 授予的进一步详情,请参阅 OAuth 2.0令牌交换。

Requesting an Access Token

有关令牌交换授予,请参考Token Exchange Request and Response协议流

OAuth2AccessTokenResponseClient 用于令牌交换授权的默认实现是 DefaultTokenExchangeTokenResponseClient,它在授权服务器的令牌端点请求访问令牌时使用 RestOperations。

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

Customizing the Access Token Request

如果需要自定义令牌请求的预处理,可以使用自定义 Converter < TokenExchangeGrantRequest,RequestEntityConverter ()提供 DefaultTokenExchangeTokenResponseClient.setRequestEntityConverter ()?>>.默认实现 TokenExchangeGrantRequestEntityConverter 构建 OAuth 2.0 Access Token Request 的 RequestEntity 表示形式。但是,提供自定义转换器将允许您扩展令牌请求并添加自定义参数。

为了只定制请求的参数,您可以使用一个定制 Converter < TokenExchangeGrantRequest,MultiValueMap < String,String > > 来提供 TokenExchangeGrantRequestEntityConverter.setParameter tersConverter () ,以完全覆盖随请求一起发送的参数。这通常比直接构造 RequestEntity 要简单。

TokenExchangeGrantRequestEntityConverter.addParameter tersConverter ()提供定制 Converter < TokenExchangeGrantRequest,MultiValueMap < String,String > > ,它构造一个聚合 Converter。

Customizing the Access Token Response

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

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

restTemplate.setErrorHandler(new OAuth2ErrorResponseErrorHandler());

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

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

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

无论您是自定义 DefaultTokenExchangeTokenResponseClient 还是提供自己的 OAuth2AccessTokenResponseClient 实现,都需要按照下面的示例进行配置:

// Customize
OAuth2AccessTokenResponseClient<TokenExchangeGrantRequest> tokenExchangeTokenResponseClient = ...

TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider = new TokenExchangeOAuth2AuthorizedClientProvider();
tokenExchangeAuthorizedClientProvider.setAccessTokenResponseClient(tokenExchangeTokenResponseClient);

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

...

authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider);

Using the Access Token

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

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:token-exchange
            scope: read
        provider:
          okta:
            token-uri: https://dev-1234.oktapreview.com/oauth2/v1/token

以及 OAuth2AuthorizedClientManager@Bean:

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

	TokenExchangeOAuth2AuthorizedClientProvider tokenExchangeAuthorizedClientProvider =
			new TokenExchangeOAuth2AuthorizedClientProvider();

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

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

	return authorizedClientManager;
}

你可按以下方式取得 OAuth2AccessToken:

@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();

		...

	}
}

TokenExchangeOAuthizedClientProvider 通过 OAuth2AuthorizationContext.getPrime ()解析主题令牌(作为 OAuth2Token)。缺省情况下使用 getPrime () ,因此在前面的示例中使用了 JwtAuthenticationToken。默认情况下不解析角色令牌。

如果需要从不同的源解析主题令牌,可以为 TokenExchangeOAuthizedClientProvider.setSubjectTokenResolver ()提供自定义函数 < OAuth2AuthorizationContext,OAuth2Token > 。

如果需要解析角色令牌,可以为 TokenExchangeOAuthizedClientProvider.setActorTokenResolver ()提供自定义函数 < OAuth2AuthorizationContext,OAuth2Token > 。

Client Authentication Support

Client Credentials

Authenticate using client_secret_basic

支持开箱即用的 HTTPBasic 客户端身份验证,无需自定义即可启用。默认实现由 DefaultOAuth2TokenRequestHeadersConverter 提供。

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

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: client-id
            client-secret: client-secret
            client-authentication-method: client_secret_basic
            authorization-grant-type: authorization_code
            ...

下面的示例演示如何配置 DefaultAuthorizationCodeTokenResponseClient 以禁用客户端凭据的 URL 编码:

DefaultOAuth2TokenRequestHeadersConverter<OAuth2AuthorizationCodeGrantRequest> headersConverter =
		new DefaultOAuth2TokenRequestHeadersConverter<>();
headersConverter.setEncodeClientCredentials(false);

OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
		new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.setHeadersConverter(headersConverter);

DefaultAuthorizationCodeTokenResponseClient tokenResponseClient =
		new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);

Authenticate using client_secret_post

客户端身份验证(客户端凭据包含在请求正文中)是开箱即用的,不需要自定义就可以启用它。

OAuth 2.0客户端注册的以下 Spring Boot 属性演示了配置:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: client-id
            client-secret: client-secret
            client-authentication-method: client_secret_post
            authorization-grant-type: authorization_code
            ...

JWT Bearer

有关 JWT 承载者客户端身份验证的详细信息,请参阅 OAuth 2.0客户端身份验证和授权授予的 JSON Web Token (JWT) Profile。

JWT Bearer Client Authentication 的默认实现是 NimbusJwtClientAuthenticationParameter tersConverter,它是一个 Converter,通过在 Client _ 断言参数中添加签名的 JSON Web Token (JWS)来定制令牌请求参数。

用于对 JWS 进行签名的 java.security. PrivateKey 或 javax.crypto. Secretariat Key 由与 NimbusJwtClientAuthenticationParameter tersConverter 关联的 com.nimbusds.jose.jwk.JWK 解析器提供。

Authenticate using private_key_jwt

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

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-authentication-method: private_key_jwt
            authorization-grant-type: authorization_code
            ...

下面的示例演示如何配置 DefaultAuthorizationCodeTokenResponseClient:

Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
	if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.PRIVATE_KEY_JWT)) {
		// Assuming RSA key type
		RSAPublicKey publicKey = ...
		RSAPrivateKey privateKey = ...
		return new RSAKey.Builder(publicKey)
				.privateKey(privateKey)
				.keyID(UUID.randomUUID().toString())
				.build();
	}
	return null;
};

OAuth2AuthorizationCodeGrantRequestEntityConverter requestEntityConverter =
		new OAuth2AuthorizationCodeGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));

DefaultAuthorizationCodeTokenResponseClient tokenResponseClient =
		new DefaultAuthorizationCodeTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);

Authenticate using client_secret_jwt

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

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: okta-client-id
            client-secret: okta-client-secret
            client-authentication-method: client_secret_jwt
            authorization-grant-type: client_credentials
            ...

下面的示例演示如何配置 DefaultClientCredenalsTokenResponseClient:

Function<ClientRegistration, JWK> jwkResolver = (clientRegistration) -> {
	if (clientRegistration.getClientAuthenticationMethod().equals(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) {
		SecretKeySpec secretKey = new SecretKeySpec(
				clientRegistration.getClientSecret().getBytes(StandardCharsets.UTF_8),
				"HmacSHA256");
		return new OctetSequenceKey.Builder(secretKey)
				.keyID(UUID.randomUUID().toString())
				.build();
	}
	return null;
};

OAuth2ClientCredentialsGrantRequestEntityConverter requestEntityConverter =
		new OAuth2ClientCredentialsGrantRequestEntityConverter();
requestEntityConverter.addParametersConverter(
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver));

DefaultClientCredentialsTokenResponseClient tokenResponseClient =
		new DefaultClientCredentialsTokenResponseClient();
tokenResponseClient.setRequestEntityConverter(requestEntityConverter);

Customizing the JWT assertion

NimbusJwtClientAuthenticationParameter tersConverter 生成的 JWT 默认包含 iss、 sub、 aud、 jti、 iat 和 exp 声明。可以通过提供 Consumer < NimbusJwtClientAuthenticationParameter tersConverter 来自定义标头和/或声明。JwtClientAuthenticationContext < T > > to setJwtClientAssertionCustomizer ().下面的例子展示了如何定制 JWT 的索赔:

Function<ClientRegistration, JWK> jwkResolver = ...

NimbusJwtClientAuthenticationParametersConverter<OAuth2ClientCredentialsGrantRequest> converter =
		new NimbusJwtClientAuthenticationParametersConverter<>(jwkResolver);
converter.setJwtClientAssertionCustomizer((context) -> {
	context.getHeaders().header("custom-header", "header-value");
	context.getClaims().claim("custom-claim", "claim-value");
});

Public Authentication

支持开箱即用的公共客户端身份验证,无需自定义即可启用。

OAuth 2.0客户端注册的以下 Spring Boot 属性演示了配置:

spring:
  security:
    oauth2:
      client:
        registration:
          okta:
            client-id: client-id
            client-authentication-method: none
            authorization-grant-type: authorization_code
            ...

使用代码交换的证明密钥(Proof Key for Code Exchange,PKCE)支持公共客户端。当客户端身份验证方法设置为“ none”(ClientAuthenticationMethod.NONE)时,将自动使用 PKCE。

Authorized Client Features

本节介绍 Spring Security 为 OAuth2客户机提供的其他特性。

Resolving an Authorized Client

@ RegisteredOAuth2AuthorizedClient 注释提供了将方法参数解析为 OAuth2AuthorizedClient 类型的参数值的能力。与使用 OAuth2AuthorizedClientManager 或 OAuth2AuthorizedClientService 访问 OAuth2AuthorizedClient 相比,这是一种方便的替代方法。下面的示例演示如何使用@RegisteredOAuth2AuthorizedClient:

@Controller
public class OAuth2ClientController {

	@GetMapping("/")
	public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
		OAuth2AccessToken accessToken = authorizedClient.getAccessToken();

		...

		return "index";
	}
}

@ RegisteredOAuth2AuthorizedClient 注释由 OAuth2AuthorizedClientArgumentResolver 处理,它直接使用 OAuth2AuthorizedClientManager,因此继承其功能。

WebClient Integration for Servlet Environments

OAuth 2.0客户端支持通过使用 ExchangeFilterfunction 与 WebClient 集成。

ServletOAuth2AuthorizedClientExchangeFilterfunction 提供了一种机制,通过使用 OAuth2AuthorizedClient 并将相关的 OAuth2AccessToken 作为承载令牌来请求受保护的资源。它直接使用 OAuth2AuthorizedClientManager,因此继承以下功能:

  • 如果客户端尚未被授权,则请求 OAuth2AccessToken。

    • authorization_code: 触发 AuthorizationRequest 重定向以启动流。
    • client_credentials: 访问令牌直接从令牌端点获取。
    • password: 访问令牌直接从令牌端点获取。
  • 如果 OAuth2AccessToken 过期,则在可以使用 OAuth2AuthorizedClientProvider 执行授权时刷新(或更新)

下面的代码显示了如何使用 OAuth 2.0客户端支持配置 WebClient 的示例:

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}

Providing the Authorized Client

ServletOAuth2AuthorizedClientExchangeFilterfunction 通过从 ClientRequest.properties ()(request Attribute)解析 OAuth2AuthorizedClient 来确定要使用的客户端(用于请求)。

下面的代码演示如何将 OAuth2AuthorizedClient 设置为请求属性:

@GetMapping("/")
public String index(@RegisteredOAuth2AuthorizedClient("okta") OAuth2AuthorizedClient authorizedClient) {
	String resourceUri = ...

	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(oauth2AuthorizedClient(authorizedClient))
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}
  1. Oauth2AuthorizedClient ()是 ServletOAuth2AuthorizedClientExchangeFilterfunction 中的静态方法。

下面的代码显示了如何将 ClientRegistration.getRegistrationId ()设置为请求属性:

@GetMapping("/")
public String index() {
	String resourceUri = ...

	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(clientRegistrationId("okta"))
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}
  1. ClientRegistrationId ()是 ServletOAuth2AuthorizedClientExchangeFilterfunction 中的静态方法。

下面的代码演示如何将身份验证设置为请求属性:

@GetMapping("/")
public String index() {
	String resourceUri = ...

	Authentication anonymousAuthentication = new AnonymousAuthenticationToken(
			"anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS"));
	String body = webClient
			.get()
			.uri(resourceUri)
			.attributes(authentication(anonymousAuthentication))
			.retrieve()
			.bodyToMono(String.class)
			.block();

	...

	return "index";
}

authentication()是 ServletOAuth2AuthorizedClientExchangeFilterfunction 中的静态方法。

建议谨慎使用此特性,因为所有 HTTP 请求都将接收绑定到所提供主体的访问令牌。

Defaulting the Authorized Client

如果没有提供 OAuth2AuthorizedClient 或 ClientRegistration.getRegistrationId ()作为请求属性,则 ServletOAuth2AuthorizedClientExchangeFilterfunction 可以根据其配置确定要使用的默认客户端。

如果配置了 setDefaultOAuth2AuthorizedClient (true)且用户已通过使用 HttpSecurity.oauth2Login ()进行身份验证,则使用与当前 OAuth2AuthenticationToken 关联的 OAuth2AccessToken。

下面的代码显示了特定的配置:

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	oauth2Client.setDefaultOAuth2AuthorizedClient(true);
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}

谨慎使用此特性,因为所有 HTTP 请求都接收访问令牌。

或者,如果 setDefaultClientRegistrationId (“ okta”)配置为有效的 ClientRegistry,则使用与 OAuth2AuthorizedClient 关联的 OAuth2AccessToken。

下面的代码显示了特定的配置:

@Bean
WebClient webClient(OAuth2AuthorizedClientManager authorizedClientManager) {
	ServletOAuth2AuthorizedClientExchangeFilterFunction oauth2Client =
			new ServletOAuth2AuthorizedClientExchangeFilterFunction(authorizedClientManager);
	oauth2Client.setDefaultClientRegistrationId("okta");
	return WebClient.builder()
			.apply(oauth2Client.oauth2Configuration())
			.build();
}

谨慎使用此特性,因为所有 HTTP 请求都接收访问令牌。

相关推荐

  1. SpringSecurity中文文档(Servlet OAuth 2.0 Client

    2024-07-11 11:14:06       19 阅读
  2. SpringSecurity中文文档(Servlet OAuth2)

    2024-07-11 11:14:06       24 阅读
  3. SpringSecurity中文文档(Servlet OAuth 2.0 Login)

    2024-07-11 11:14:06       19 阅读
  4. SpringSecurity

    2024-07-11 11:14:06       34 阅读
  5. Apache 开源项目文档中心 (英文 + 中文)

    2024-07-11 11:14:06       28 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 11:14:06       66 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 11:14:06       70 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 11:14:06       57 阅读
  4. Python语言-面向对象

    2024-07-11 11:14:06       68 阅读

热门阅读

  1. Linux串口设备的使用<ubuntu>

    2024-07-11 11:14:06       21 阅读
  2. 计算机如何学习

    2024-07-11 11:14:06       19 阅读
  3. 【通信原理】matlab中pskmod的介绍

    2024-07-11 11:14:06       17 阅读
  4. Perl词法分析:构建编程语言解析器的指南

    2024-07-11 11:14:06       23 阅读
  5. Elasticsearch 搜索模板:重用和共享查询

    2024-07-11 11:14:06       25 阅读
  6. Spring Boot 自动装配原理

    2024-07-11 11:14:06       17 阅读
  7. JWT重放漏洞攻防策略

    2024-07-11 11:14:06       22 阅读
  8. QT跨平台开发(windows、mac)中.pro文件设置

    2024-07-11 11:14:06       19 阅读
  9. thinkphp:数据库复合查询-OR的使用

    2024-07-11 11:14:06       19 阅读
  10. 32. 小批量梯度下降法(Mini-batch Gradient Descent)

    2024-07-11 11:14:06       23 阅读
  11. MySQL相关函数

    2024-07-11 11:14:06       19 阅读