配置模型

默认配置

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

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

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

}
授权码授权模式 需要对资源所有者进行身份验证。因此,**必须**除了默认的 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 UserInfo 端点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 UserInfo 端点 的配置器。
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,提供对默认和(可选)添加的AuthenticationConverter列表的访问,允许添加、删除或自定义特定的AuthenticationConverter
3 authenticationProvider():添加一个AuthenticationProvider主处理器),用于对OAuth2ClientAuthenticationToken进行身份验证。
4 authenticationProviders():设置Consumer,提供对默认和(可选)添加的AuthenticationProvider列表的访问,允许添加、删除或自定义特定的AuthenticationProvider
5 authenticationSuccessHandler():用于处理成功的客户端身份验证并将OAuth2ClientAuthenticationToken关联到SecurityContextAuthenticationSuccessHandler后处理器)。
6 errorResponseHandler():用于处理客户端身份验证失败并返回OAuth2Error响应AuthenticationFailureHandler后处理器)。

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 令牌进行身份验证。

自定义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 访问令牌将在顶级cnf(确认方法)声明中包含一个x5t#S256声明,其中包含X509Certificate指纹。

将访问令牌绑定到客户端的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();