配置模型

默认配置

OAuth2AuthorizationServerConfiguration 是一个 @Configuration,它为 OAuth2 授权服务器提供了最小的默认配置。

OAuth2AuthorizationServerConfiguration 使用 OAuth2AuthorizationServerConfigurer 来应用默认配置并注册一个 SecurityFilterChain @Bean,该 @Bean 由支持 OAuth2 授权服务器的所有基础设施组件组成。

OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(HttpSecurity) 是一个方便的(static)实用程序方法,它将默认的 OAuth2 安全配置应用于 HttpSecurity

OAuth2 授权服务器 SecurityFilterChain @Bean 配置了以下默认协议端点

只有在注册了 JWKSource<SecurityContext> @Bean 时才会配置 JWK 集端点。

以下示例展示了如何使用OAuth2AuthorizationServerConfiguration来应用最小的默认配置。

@Configuration
@Import(OAuth2AuthorizationServerConfiguration.class)
public class AuthorizationServerConfig {

	@Bean
	public RegisteredClientRepository registeredClientRepository() {
		List<RegisteredClient> registrations = ...
		return new InMemoryRegisteredClientRepository(registrations);
	}

	@Bean
	public JWKSource<SecurityContext> jwkSource() {
		RSAKey rsaKey = ...
		JWKSet jwkSet = new JWKSet(rsaKey);
		return (jwkSelector, securityContext) -> jwkSelector.select(jwkSet);
	}

}
授权码授权模式(authorization_code grant)要求资源所有者进行身份验证。因此,除了默认的 OAuth2 安全配置之外,还**必须**配置用户身份验证机制。

默认配置中禁用了OpenID Connect 1.0。以下示例展示了如何通过初始化OidcConfigurer来启用 OpenID Connect 1.0。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http);

	http.getConfigurer(OAuth2AuthorizationServerConfigurer.class)
		.oidc(Customizer.withDefaults());	// Initialize `OidcConfigurer`

	return http.build();
}

除了默认的协议端点之外,OAuth2 授权服务器的SecurityFilterChain @Bean还配置了以下 OpenID Connect 1.0 协议端点。

默认情况下,OpenID Connect 1.0 客户端注册端点被禁用,因为许多部署不需要动态客户端注册。
OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource<SecurityContext>)是一个方便的(static)实用程序方法,可用于注册JwtDecoder @Bean,这对于OpenID Connect 1.0 用户信息端点OpenID Connect 1.0 客户端注册端点是**必需的**。

以下示例展示了如何注册JwtDecoder @Bean

@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
	return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}

OAuth2AuthorizationServerConfiguration的主要目的是提供一种方便的方法来应用 OAuth2 授权服务器的最小默认配置。但是,在大多数情况下,都需要自定义配置。

自定义配置

OAuth2AuthorizationServerConfigurer提供了完全自定义 OAuth2 授权服务器安全配置的能力。它允许您指定要使用的核心组件,例如RegisteredClientRepositoryOAuth2AuthorizationServiceOAuth2TokenGenerator等。此外,它还允许您自定义协议端点的请求处理逻辑,例如授权端点设备授权端点设备验证端点令牌端点令牌自省端点等。

OAuth2AuthorizationServerConfigurer提供了以下配置选项。

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.registeredClientRepository(registeredClientRepository) (1)
		.authorizationService(authorizationService) (2)
		.authorizationConsentService(authorizationConsentService)   (3)
		.authorizationServerSettings(authorizationServerSettings) (4)
		.tokenGenerator(tokenGenerator) (5)
		.clientAuthentication(clientAuthentication -> { })  (6)
		.authorizationEndpoint(authorizationEndpoint -> { })    (7)
		.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { })    (8)
		.deviceVerificationEndpoint(deviceVerificationEndpoint -> { })  (9)
		.tokenEndpoint(tokenEndpoint -> { })    (10)
		.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { })  (11)
		.tokenRevocationEndpoint(tokenRevocationEndpoint -> { })    (12)
		.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { })    (13)
		.oidc(oidc -> oidc
			.providerConfigurationEndpoint(providerConfigurationEndpoint -> { })    (14)
			.logoutEndpoint(logoutEndpoint -> { })  (15)
			.userInfoEndpoint(userInfoEndpoint -> { })  (16)
			.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })  (17)
		);

	return http.build();
}
1 registeredClientRepository():用于管理新旧客户端的RegisteredClientRepository(**必需**)。
2 authorizationService(): 用于管理新授权和现有授权的 OAuth2AuthorizationService
3 authorizationConsentService(): 用于管理新授权同意和现有授权同意的 OAuth2AuthorizationConsentService
4 authorizationServerSettings(): 用于自定义 OAuth2 授权服务器配置设置的 AuthorizationServerSettings (**必需**)。
5 tokenGenerator(): 用于生成 OAuth2 授权服务器支持的令牌的 OAuth2TokenGenerator
6 clientAuthentication(): OAuth2 客户端身份验证 的配置器。
7 authorizationEndpoint(): OAuth2 授权端点 的配置器。
8 deviceAuthorizationEndpoint(): OAuth2 设备授权端点 的配置器。
9 deviceVerificationEndpoint(): OAuth2 设备验证端点 的配置器。
10 tokenEndpoint(): OAuth2 令牌端点 的配置器。
11 tokenIntrospectionEndpoint(): OAuth2 令牌自省端点 的配置器。
12 tokenRevocationEndpoint(): OAuth2 令牌撤销端点 的配置器。
13 authorizationServerMetadataEndpoint(): OAuth2 授权服务器元数据端点 的配置器。
14 providerConfigurationEndpoint(): OpenID Connect 1.0 提供者配置端点 的配置器。
15 logoutEndpoint(): OpenID Connect 1.0 注销端点 的配置器。
16 userInfoEndpoint(): OpenID Connect 1.0 用户信息端点 的配置器。
17 clientRegistrationEndpoint(): OpenID Connect 1.0 客户端注册端点 的配置器。

配置授权服务器设置

AuthorizationServerSettings 包含 OAuth2 授权服务器的配置设置。它指定协议端点的 URI 以及 颁发者标识符。协议端点的默认 URI 如下所示

public final class AuthorizationServerSettings extends AbstractSettings {

	...

	public static Builder builder() {
		return new Builder()
			.authorizationEndpoint("/oauth2/authorize")
			.deviceAuthorizationEndpoint("/oauth2/device_authorization")
			.deviceVerificationEndpoint("/oauth2/device_verification")
			.tokenEndpoint("/oauth2/token")
			.tokenIntrospectionEndpoint("/oauth2/introspect")
			.tokenRevocationEndpoint("/oauth2/revoke")
			.jwkSetEndpoint("/oauth2/jwks")
			.oidcLogoutEndpoint("/connect/logout")
			.oidcUserInfoEndpoint("/userinfo")
			.oidcClientRegistrationEndpoint("/connect/register");
	}

	...

}
AuthorizationServerSettings 是一个**必需**的组件。
@Import(OAuth2AuthorizationServerConfiguration.class) 会自动注册一个 AuthorizationServerSettings @Bean,如果尚未提供。

以下示例展示了如何自定义配置设置并注册一个 AuthorizationServerSettings @Bean

@Bean
public AuthorizationServerSettings authorizationServerSettings() {
	return AuthorizationServerSettings.builder()
		.issuer("https://example.com")
		.authorizationEndpoint("/oauth2/v1/authorize")
		.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
		.deviceVerificationEndpoint("/oauth2/v1/device_verification")
		.tokenEndpoint("/oauth2/v1/token")
		.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
		.tokenRevocationEndpoint("/oauth2/v1/revoke")
		.jwkSetEndpoint("/oauth2/v1/jwks")
		.oidcLogoutEndpoint("/connect/v1/logout")
		.oidcUserInfoEndpoint("/connect/v1/userinfo")
		.oidcClientRegistrationEndpoint("/connect/v1/register")
		.build();
}

AuthorizationServerContext 是一个上下文对象,它保存授权服务器运行时环境的信息。它提供对 AuthorizationServerSettings 和“当前”颁发者标识符的访问。

如果颁发者标识符未在 AuthorizationServerSettings.builder().issuer(String) 中配置,则会从当前请求中解析。
AuthorizationServerContext 可通过 AuthorizationServerContextHolder 访问,它使用 ThreadLocal 将其与当前请求线程关联。

配置客户端身份验证

OAuth2ClientAuthenticationConfigurer 提供了自定义 OAuth2 客户端身份验证 的能力。它定义了扩展点,允许您自定义客户端身份验证请求的预处理、主处理和后处理逻辑。

OAuth2ClientAuthenticationConfigurer 提供以下配置选项

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.clientAuthentication(clientAuthentication ->
			clientAuthentication
				.authenticationConverter(authenticationConverter)   (1)
				.authenticationConverters(authenticationConvertersConsumer) (2)
				.authenticationProvider(authenticationProvider) (3)
				.authenticationProviders(authenticationProvidersConsumer)   (4)
				.authenticationSuccessHandler(authenticationSuccessHandler) (5)
				.errorResponseHandler(errorResponseHandler) (6)
		);

	return http.build();
}
1 authenticationConverter():添加一个 AuthenticationConverter预处理器),用于在尝试从 HttpServletRequest 中提取客户端凭据到 OAuth2ClientAuthenticationToken 实例时使用。
2 authenticationConverters():设置 Consumer,提供对默认和(可选)添加的 AuthenticationConverterList 的访问,允许添加、删除或自定义特定 AuthenticationConverter
3 authenticationProvider():添加一个 AuthenticationProvider主处理器),用于对 OAuth2ClientAuthenticationToken 进行身份验证。
4 authenticationProviders():设置 Consumer,提供对默认和(可选)添加的 AuthenticationProviderList 的访问,允许添加、删除或自定义特定 AuthenticationProvider
5 authenticationSuccessHandler()AuthenticationSuccessHandler后处理器),用于处理成功的客户端身份验证并将 OAuth2ClientAuthenticationTokenSecurityContext 关联。
6 errorResponseHandler()AuthenticationFailureHandler后处理器),用于处理失败的客户端身份验证并返回 OAuth2Error 响应

OAuth2ClientAuthenticationConfigurer 配置 OAuth2ClientAuthenticationFilter 并将其注册到 OAuth2 授权服务器 SecurityFilterChain @Bean 中。OAuth2ClientAuthenticationFilter 是处理客户端身份验证请求的 Filter

默认情况下,客户端身份验证对于 OAuth2 令牌端点OAuth2 令牌自检端点OAuth2 令牌撤销端点 是必需的。支持的客户端身份验证方法是 client_secret_basicclient_secret_postprivate_key_jwtclient_secret_jwttls_client_authself_signed_tls_client_authnone(公共客户端)。

OAuth2ClientAuthenticationFilter 使用以下默认值进行配置

  • AuthenticationConverter — 由 JwtClientAssertionAuthenticationConverterX509ClientCertificateAuthenticationConverterClientSecretBasicAuthenticationConverterClientSecretPostAuthenticationConverterPublicClientAuthenticationConverter 组成的 DelegatingAuthenticationConverter

  • AuthenticationManager — 由 JwtClientAssertionAuthenticationProviderX509ClientCertificateAuthenticationProviderClientSecretAuthenticationProviderPublicClientAuthenticationProvider 组成的 AuthenticationManager

  • AuthenticationSuccessHandler — 将“已验证”的 OAuth2ClientAuthenticationToken(当前 Authentication)与 SecurityContext 关联的内部实现。

  • AuthenticationFailureHandler — 使用与 OAuth2AuthenticationException 关联的 OAuth2Error 返回 OAuth2 错误响应的内部实现。

自定义 Jwt 客户端断言验证

JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY 是默认工厂,它为指定的 RegisteredClient 提供一个 OAuth2TokenValidator<Jwt>,用于验证 Jwt 客户端断言的 isssubaudexpnbf 声明。

JwtClientAssertionDecoderFactory 提供了通过向 setJwtValidatorFactory() 提供类型为 Function<RegisteredClient, OAuth2TokenValidator<Jwt>> 的自定义工厂来覆盖默认 Jwt 客户端断言验证的功能。

JwtClientAssertionDecoderFactoryJwtClientAssertionAuthenticationProvider 使用的默认 JwtDecoderFactory,它为指定的 RegisteredClient 提供一个 JwtDecoder,用于在 OAuth2 客户端身份验证期间对 Jwt Bearer Token 进行身份验证。

自定义 JwtClientAssertionDecoderFactory 的一个常见用例是验证 Jwt 客户端断言中的其他声明。

以下示例展示了如何使用自定义的 JwtClientAssertionDecoderFactory 配置 JwtClientAssertionAuthenticationProvider,该工厂验证 Jwt 客户端断言中的其他声明

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
		new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
		.clientAuthentication(clientAuthentication ->
			clientAuthentication
				.authenticationProviders(configureJwtClientAssertionValidator())
		);

	return http.build();
}

private Consumer<List<AuthenticationProvider>> configureJwtClientAssertionValidator() {
	return (authenticationProviders) ->
		authenticationProviders.forEach((authenticationProvider) -> {
			if (authenticationProvider instanceof JwtClientAssertionAuthenticationProvider) {
				// Customize JwtClientAssertionDecoderFactory
				JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory();
				Function<RegisteredClient, OAuth2TokenValidator<Jwt>> jwtValidatorFactory = (registeredClient) ->
					new DelegatingOAuth2TokenValidator<>(
						// Use default validators
						JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY.apply(registeredClient),
						// Add custom validator
						new JwtClaimValidator<>("claim", "value"::equals));
				jwtDecoderFactory.setJwtValidatorFactory(jwtValidatorFactory);

				((JwtClientAssertionAuthenticationProvider) authenticationProvider)
					.setJwtDecoderFactory(jwtDecoderFactory);
			}
		});
}

自定义双向 TLS 客户端身份验证

X509ClientCertificateAuthenticationProvider 用于验证在 OAuth2 客户端身份验证期间使用 ClientAuthenticationMethod.TLS_CLIENT_AUTHClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH 方法时接收到的客户端 X509Certificate 链。它还与一个“证书验证器”组合在一起,该验证器用于在 TLS 握手成功完成之后验证客户端 X509Certificate 的内容。

PKI 双向 TLS 方法

对于 PKI 双向 TLS (ClientAuthenticationMethod.TLS_CLIENT_AUTH) 方法,证书验证器的默认实现会根据设置 RegisteredClient.getClientSettings.getX509CertificateSubjectDN() 验证客户端 X509Certificate 的主体识别名称。

如果您需要验证客户端 X509Certificate 的其他属性,例如主体备用名称 (SAN) 条目,以下示例展示了如何使用证书验证器的自定义实现配置 X509ClientCertificateAuthenticationProvider

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
			new OAuth2AuthorizationServerConfigurer();
	http.apply(authorizationServerConfigurer);

	authorizationServerConfigurer
			.clientAuthentication(clientAuthentication ->
					clientAuthentication
							.authenticationProviders(configureX509ClientCertificateVerifier())
			);

	return http.build();
}

private Consumer<List<AuthenticationProvider>> configureX509ClientCertificateVerifier() {
	return (authenticationProviders) ->
			authenticationProviders.forEach((authenticationProvider) -> {
				if (authenticationProvider instanceof X509ClientCertificateAuthenticationProvider) {
					Consumer<OAuth2ClientAuthenticationContext> certificateVerifier = (clientAuthenticationContext) -> {
						OAuth2ClientAuthenticationToken clientAuthentication = clientAuthenticationContext.getAuthentication();
						RegisteredClient registeredClient = clientAuthenticationContext.getRegisteredClient();
						X509Certificate[] clientCertificateChain = (X509Certificate[]) clientAuthentication.getCredentials();
						X509Certificate clientCertificate = clientCertificateChain[0];

						// TODO Verify Subject Alternative Name (SAN) entry

					};

					((X509ClientCertificateAuthenticationProvider) authenticationProvider)
							.setCertificateVerifier(certificateVerifier);
				}
			});
}

自签名证书双向 TLS 方法

对于自签名证书双向 TLS (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH) 方法,证书验证器的默认实现将使用设置 RegisteredClient.getClientSettings.getJwkSetUrl() 获取客户端的 JSON Web 密钥集,并期望在 TLS 握手期间接收到的客户端 X509Certificate 中找到匹配项。

RegisteredClient.getClientSettings.getJwkSetUrl() 设置用于通过 JSON Web 密钥 (JWK) 集检索客户端的证书。证书使用集合中单个 JWK 的 x5c 参数表示。

客户端证书绑定访问令牌

当在令牌端点使用双向 TLS 客户端身份验证时,授权服务器能够将颁发的访问令牌绑定到客户端的 X509Certificate。绑定是通过计算客户端 X509Certificate 的 SHA-256 指纹并将指纹与访问令牌关联来完成的。例如,JWT 访问令牌将包含一个 x5t#S256 声明,该声明包含 X509Certificate 指纹,位于顶层 cnf (确认方法) 声明中。

将访问令牌绑定到客户端的 X509Certificate 提供了在受保护资源访问期间实现拥有权证明机制的能力。例如,受保护资源将获取在双向 TLS 身份验证期间使用的客户端 X509Certificate,然后验证证书指纹是否与与访问令牌关联的 x5t#S256 声明匹配。

以下示例展示了如何为客户端启用证书绑定访问令牌

RegisteredClient mtlsClient = RegisteredClient.withId(UUID.randomUUID().toString())
		.clientId("mtls-client")
		.clientAuthenticationMethod(ClientAuthenticationMethod.TLS_CLIENT_AUTH)
		.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
		.scope("scope-a")
		.clientSettings(
				ClientSettings.builder()
						.x509CertificateSubjectDN("CN=mtls-client,OU=Spring Samples,O=Spring,C=US")
						.build()
		)
		.tokenSettings(
				TokenSettings.builder()
						.x509CertificateBoundAccessTokens(true)
						.build()
		)
		.build();