Core Interfaces and Classes

本节介绍了 Spring Security 提供的 OAuth2 核心接口和类。

This section describes the OAuth2 core interfaces and classes that Spring Security offers.

ClientRegistration

OAuth2AccessToken 是向 OAuth 2.0 或 OpenID Connect 1.0 提供程序注册的客户端的表示形式。

ClientRegistration is a representation of a client registered with an OAuth 2.0 or OpenID Connect 1.0 Provider.

OAuth2AuthorizedClient 对象包含诸如客户端 ID、客户端密钥、授权授予类型、重定向 URI、范围、授权 URI、令牌 URI 等信息和其他详细信息。

A ClientRegistration object holds information, such as client id, client secret, authorization grant type, redirect URI, scope(s), authorization URI, token URI, and other details.

ClientRegistration 及其属性被定义如下:

ClientRegistration and its properties are defined as follows:

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: The ID that uniquely identifies the ClientRegistration.
2 clientId: The client identifier.
3 clientSecret: The client secret.
4 clientAuthenticationMethod: The method used to authenticate the Client with the Provider. The supported values are client_secret_basic, client_secret_post, private_key_jwt, client_secret_jwt and none (public clients).
5 authorizationGrantType: The OAuth 2.0 Authorization Framework defines four Authorization Grant types. The supported values are authorization_code, client_credentials, password, as well as, extension grant type urn:ietf:params:oauth:grant-type:jwt-bearer.
6 redirectUri: The client’s registered redirect URI that the Authorization Server redirects the end-user’s user-agent to after the end-user has authenticated and authorized access to the client.
7 scopes: The scope(s) requested by the client during the Authorization Request flow, such as openid, email, or profile.
8 clientName: A descriptive name used for the client. The name may be used in certain scenarios, such as when displaying the name of the client in the auto-generated login page.
9 authorizationUri: The Authorization Endpoint URI for the Authorization Server.
10 tokenUri: The Token Endpoint URI for the Authorization Server.
11 jwkSetUri: The URI used to retrieve the JSON Web Key (JWK) Set from the Authorization Server, which contains the cryptographic key(s) used to verify the JSON Web Signature (JWS) of the ID Token and (optionally) the UserInfo Response.
12 issuerUri: Returns the issuer identifier URI for the OpenID Connect 1.0 provider or the OAuth 2.0 Authorization Server.
13 configurationMetadata: The OpenID Provider Configuration Information. This information is available only if the Spring Boot 2.x property spring.security.oauth2.client.provider.[providerId].issuerUri is configured.
14 (userInfoEndpoint)uri: The UserInfo Endpoint URI used to access the claims and attributes of the authenticated end-user.
15 (userInfoEndpoint)authenticationMethod: The authentication method used when sending the access token to the UserInfo Endpoint. The supported values are header, form, and query.
16 userNameAttributeName: The name of the attribute returned in the UserInfo Response that references the Name or Identifier of the end-user.

你可以通过发现 OpenID Connect 提供程序的 ClientRegistration 或授权服务器的 ClientRegistration 初始配置 ClientRegistration

You can initially configure a ClientRegistration by using discovery of an OpenID Connect Provider’s Configuration endpoint or an Authorization Server’s Metadata endpoint.

ClientRegistrations 提供方便的方法来按如下方式配置 ClientRegistration

ClientRegistrations provides convenience methods for configuring a ClientRegistration in this way, as follows:

  • Java

  • Kotlin

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

上述代码按顺序查询 ClientRegistrationsClientRegistrationhttps://idp.example.com/issuer/.well-known/openid-configuration,在第一个返回 200 响应时停止。

作为备用办法,您可以使用 ClientRegistrations.fromOidcIssuerLocation() 仅查询 OpenID Connect 提供程序的配置端点。

As an alternative, you can use ClientRegistrations.fromOidcIssuerLocation() to query only the OpenID Connect Provider’s Configuration endpoint.

ClientRegistrationRepository

ClientRegistrationRepository 作为 OAuth 2.0/OpenID Connect 1.0 ClientRegistration 的储存库。

The ClientRegistrationRepository serves as a repository for OAuth 2.0 / OpenID Connect 1.0 ClientRegistration(s).

客户端注册信息最终由关联的授权服务器存储和拥有。该存储库提供了检索主要客户端注册信息子集的能力,该子集存储在授权服务器中。

Client registration information is ultimately stored and owned by the associated Authorization Server. This repository provides the ability to retrieve a subset of the primary client registration information, which is stored with the Authorization Server.

Spring Boot 2.x 自动配置将 spring.security.oauth2.client.registration.[registrationId] 下的各个属性绑定到 ClientRegistration 的实例,然后将每个 ClientRegistration 实例组成到 ClientRegistrationRepository 中。

Spring Boot 2.x auto-configuration binds each of the properties under spring.security.oauth2.client.registration.[registrationId] to an instance of ClientRegistration and then composes each of the ClientRegistration instance(s) within a ClientRegistrationRepository.

ClientRegistrationRepository 的默认实现是 InMemoryClientRegistrationRepository

The default implementation of ClientRegistrationRepository is InMemoryClientRegistrationRepository.

自动配置还会在 ApplicationContext 中将 ClientRegistrationRepository 作为 @Bean 注册,以便它可用于依赖项注入(如果应用程序需要)。

The auto-configuration also registers the ClientRegistrationRepository as a @Bean in the ApplicationContext so that it is available for dependency injection, if needed by the application.

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

The following listing shows an example:

  • Java

  • Kotlin

@Controller
public class OAuth2ClientController {

	@Autowired
	private ClientRegistrationRepository clientRegistrationRepository;

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

		...

		return "index";
	}
}
@Controller
class OAuth2ClientController {

    @Autowired
    private lateinit var clientRegistrationRepository: ClientRegistrationRepository

    @GetMapping("/")
    fun index(): String {
        val oktaRegistration =
                this.clientRegistrationRepository.findByRegistrationId("okta")

        //...

        return "index";
    }
}

OAuth2AuthorizedClient

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

OAuth2AuthorizedClient is a representation of an Authorized Client. A client is considered to be authorized when the end-user (the Resource Owner) has granted authorization to the client to access its protected resources.

OAuth2AuthorizedClient 的作用是将 OAuth2AccessToken(和可选的 OAuth2RefreshToken)关联到 ClientRegistration(客户端)和资源所有者(即授予授权的 Principal 最终用户)。

OAuth2AuthorizedClient serves the purpose of associating an OAuth2AccessToken (and optional OAuth2RefreshToken) to a ClientRegistration (client) and resource owner, who is the Principal end-user that granted the authorization.

OAuth2AuthorizedClientRepository and OAuth2AuthorizedClientService

OAuth2AuthorizedClientRepository 负责在 Web 请求之间持续存在 OAuth2AuthorizedClient,而 OAuth2AuthorizedClientService 的主要作用是在应用程序级别管理 OAuth2AuthorizedClient

OAuth2AuthorizedClientRepository is responsible for persisting OAuth2AuthorizedClient(s) between web requests, whereas the primary role of OAuth2AuthorizedClientService is to manage OAuth2AuthorizedClient(s) at the application-level.

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

From a developer perspective, the OAuth2AuthorizedClientRepository or OAuth2AuthorizedClientService provides the ability to look up an OAuth2AccessToken associated with a client so that it can be used to initiate a protected resource request.

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

The following listing shows an example:

  • Java

  • Kotlin

@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";
    }
}
@Controller
class OAuth2ClientController {

    @Autowired
    private lateinit var authorizedClientService: OAuth2AuthorizedClientService

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

        ...

        return "index";
    }
}

Spring Boot 2.x 自动配置在 ApplicationContext 中注册了一个 OAuth2AuthorizedClientRepository 或一个 OAuth2AuthorizedClientService @Bean。但是,应用程序可以覆盖并注册一个自定义 OAuth2AuthorizedClientRepositoryOAuth2AuthorizedClientService @Bean

Spring Boot 2.x auto-configuration registers an OAuth2AuthorizedClientRepository or an OAuth2AuthorizedClientService @Bean in the ApplicationContext. However, the application can override and register a custom OAuth2AuthorizedClientRepository or OAuth2AuthorizedClientService @Bean.

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

The default implementation of OAuth2AuthorizedClientService is InMemoryOAuth2AuthorizedClientService, which stores OAuth2AuthorizedClient objects in-memory.

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

Alternatively, you can configure the JDBC implementation JdbcOAuth2AuthorizedClientService to persist OAuth2AuthorizedClient instances in a database.

JdbcOAuth2AuthorizedClientService 取决于 OAuth 2.0 Client Schema 中描述的表定义。

JdbcOAuth2AuthorizedClientService depends on the table definition described in OAuth 2.0 Client Schema.

OAuth2AuthorizedClientManager and OAuth2AuthorizedClientProvider

OAuth2AuthorizedClientManager 负责 OAuth2AuthorizedClient 的整体管理。

The OAuth2AuthorizedClientManager is responsible for the overall management of OAuth2AuthorizedClient(s).

主要职责包括:

The primary responsibilities include:

  • Authorizing (or re-authorizing) an OAuth 2.0 Client, by using an OAuth2AuthorizedClientProvider.

  • Delegating the persistence of an OAuth2AuthorizedClient, typically by using an OAuth2AuthorizedClientService or OAuth2AuthorizedClientRepository.

  • Delegating to an OAuth2AuthorizationSuccessHandler when an OAuth 2.0 Client has been successfully authorized (or re-authorized).

  • Delegating to an OAuth2AuthorizationFailureHandler when an OAuth 2.0 Client fails to authorize (or re-authorize).

OAuth2AuthorizedClientProvider 实现了一个用于授权(或重新授权)OAuth 2.0 客户端的策略。实现通常实现授权赠款类型,例如 authorization_codeclient_credentials 和其他类型。

An OAuth2AuthorizedClientProvider implements a strategy for authorizing (or re-authorizing) an OAuth 2.0 Client. Implementations typically implement an authorization grant type, such as authorization_code, client_credentials, and others.

OAuth2AuthorizedClientManager 的默认实现是 DefaultOAuth2AuthorizedClientManager,它与 OAuth2AuthorizedClientProvider 相关联,该后者可能使用基于委托的组合支持多个授权赠款类型。您可以使用 OAuth2AuthorizedClientProviderBuilder 配置和构建基于委托的组合。

The default implementation of OAuth2AuthorizedClientManager is DefaultOAuth2AuthorizedClientManager, which is associated with an OAuth2AuthorizedClientProvider that may support multiple authorization grant types using a delegation-based composite. You can use OAuth2AuthorizedClientProviderBuilder to configure and build the delegation-based composite.

以下代码展示了如何配置和构建 OAuth2AuthorizedClientProvider 组合的一个示例,该组合为 authorization_coderefresh_tokenclient_credentialspassword 授权赠款类型提供支持:

The following code shows an example of how to configure and build an OAuth2AuthorizedClientProvider composite that provides support for the authorization_code, refresh_token, client_credentials, and password authorization grant types:

  • Java

  • Kotlin

@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;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientRepository: OAuth2AuthorizedClientRepository): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .authorizationCode()
            .refreshToken()
            .clientCredentials()
            .password()
            .build()
    val authorizedClientManager = DefaultOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientRepository)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}

当授权尝试成功时,DefaultOAuth2AuthorizedClientManager 将委托给 OAuth2AuthorizationSuccessHandler,后者(默认情况下)通过 OAuth2AuthorizedClientRepository 保存 OAuth2AuthorizedClient。如果重新授权失败(例如,刷新令牌不再有效),先前保存的 OAuth2AuthorizedClient 将通过 RemoveAuthorizedClientOAuth2AuthorizationFailureHandlerOAuth2AuthorizedClientRepository 中移除。您可以通过 setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler)setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler) 自定义默认行为。

When an authorization attempt succeeds, the DefaultOAuth2AuthorizedClientManager delegates to the OAuth2AuthorizationSuccessHandler, which (by default) saves the OAuth2AuthorizedClient through the OAuth2AuthorizedClientRepository. In the case of a re-authorization failure (for example, a refresh token is no longer valid), the previously saved OAuth2AuthorizedClient is removed from the OAuth2AuthorizedClientRepository through the RemoveAuthorizedClientOAuth2AuthorizationFailureHandler. You can customize the default behavior through setAuthorizationSuccessHandler(OAuth2AuthorizationSuccessHandler) and setAuthorizationFailureHandler(OAuth2AuthorizationFailureHandler).

DefaultOAuth2AuthorizedClientManager 还与类型为 Function<OAuth2AuthorizeRequest, Map<String, Object>>contextAttributesMapper 关联,后者负责将来自 OAuth2AuthorizeRequest 的属性映射到要与 OAuth2AuthorizationContext 关联的属性 Map。这在需要为 OAuth2AuthorizedClientProvider 提供必需的(支持的)属性时很有用,例如。PasswordOAuth2AuthorizedClientProvider 要求 usernamepassword 资源所有者的 OAuth2AuthorizationContext.getAttributes() 中可用。

The DefaultOAuth2AuthorizedClientManager is also associated with a contextAttributesMapper of type Function<OAuth2AuthorizeRequest, Map<String, Object>>, which is responsible for mapping attribute(s) from the OAuth2AuthorizeRequest to a Map of attributes to be associated to the OAuth2AuthorizationContext. This can be useful when you need to supply an OAuth2AuthorizedClientProvider with required (supported) attribute(s), eg. the PasswordOAuth2AuthorizedClientProvider requires the resource owner’s username and password to be available in OAuth2AuthorizationContext.getAttributes().

以下代码显示了 contextAttributesMapper 的示例:

The following code shows an example of the contextAttributesMapper:

  • 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: String = servletRequest.getParameter(OAuth2ParameterNames.USERNAME)
        val password: String = 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
    }
}

DefaultOAuth2AuthorizedClientManager 被设计为在 HttpServletRequest 上下文中 within 使用的。当在 HttpServletRequest 上下文的 outside 中操作时,改用 AuthorizedClientServiceOAuth2AuthorizedClientManager

The DefaultOAuth2AuthorizedClientManager is designed to be used within the context of a HttpServletRequest. When operating outside of a HttpServletRequest context, use AuthorizedClientServiceOAuth2AuthorizedClientManager instead.

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

A service application is a common use case for when to use an AuthorizedClientServiceOAuth2AuthorizedClientManager. Service applications often run in the background, without any user interaction, and typically run under a system-level account instead of a user account. An OAuth 2.0 Client configured with the client_credentials grant type can be considered a type of service application.

以下代码显示了如何配置支持 client_credentials 授权类型的 AuthorizedClientServiceOAuth2AuthorizedClientManager 的示例:

The following code shows an example of how to configure an AuthorizedClientServiceOAuth2AuthorizedClientManager that provides support for the client_credentials grant type:

  • Java

  • Kotlin

@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;
}
@Bean
fun authorizedClientManager(
        clientRegistrationRepository: ClientRegistrationRepository,
        authorizedClientService: OAuth2AuthorizedClientService): OAuth2AuthorizedClientManager {
    val authorizedClientProvider = OAuth2AuthorizedClientProviderBuilder.builder()
            .clientCredentials()
            .build()
    val authorizedClientManager = AuthorizedClientServiceOAuth2AuthorizedClientManager(
            clientRegistrationRepository, authorizedClientService)
    authorizedClientManager.setAuthorizedClientProvider(authorizedClientProvider)
    return authorizedClientManager
}