配置模型

默认配置

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

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

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

JWK Set 端点仅在注册了 JWKSource<SecurityContext> @Bean 时才配置。

以下示例展示了如何使用 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 {
	http
		.oauth2AuthorizationServer((authorizationServer) ->
			authorizationServer
				.oidc(Customizer.withDefaults())	// Initialize `OidcConfigurer`
		);
	return http.build();
}

除了默认协议端点之外,OAuth2 授权服务器 SecurityFilterChain @Bean 还配置了以下 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 {
	http
		.oauth2AuthorizationServer((authorizationServer) ->
			authorizationServer
				.registeredClientRepository(registeredClientRepository)	(1)
				.authorizationService(authorizationService)	(2)
				.authorizationConsentService(authorizationConsentService)	(3)
				.authorizationServerSettings(authorizationServerSettings)	(4)
				.tokenGenerator(tokenGenerator)	(5)
				.clientAuthentication(clientAuthentication -> { })	(6)
				.authorizationEndpoint(authorizationEndpoint -> { })	(7)
				.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint -> { })  (8)
				.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { })	(9)
				.deviceVerificationEndpoint(deviceVerificationEndpoint -> { })	(10)
				.tokenEndpoint(tokenEndpoint -> { })	(11)
				.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { })	(12)
				.tokenRevocationEndpoint(tokenRevocationEndpoint -> { })	(13)
				.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })  (14)
				.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { })	(15)
				.oidc(oidc -> oidc
					.providerConfigurationEndpoint(providerConfigurationEndpoint -> { })	(16)
					.logoutEndpoint(logoutEndpoint -> { })	(17)
					.userInfoEndpoint(userInfoEndpoint -> { })	(18)
					.clientRegistrationEndpoint(clientRegistrationEndpoint -> { })	(19)
				)
		);

	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 pushedAuthorizationRequestEndpoint()OAuth2 推送授权请求端点 的配置器。
9 deviceAuthorizationEndpoint()OAuth2 设备授权端点 的配置器。
10 deviceVerificationEndpoint()OAuth2 设备验证端点 的配置器。
11 tokenEndpoint()OAuth2 令牌端点 的配置器。
12 tokenIntrospectionEndpoint()OAuth2 令牌内省端点 的配置器。
13 tokenRevocationEndpoint()OAuth2 令牌撤销端点 的配置器。
14 clientRegistrationEndpoint()OAuth2 客户端注册端点 的配置器。
15 authorizationServerMetadataEndpoint()OAuth2 授权服务器元数据端点 的配置器。
16 providerConfigurationEndpoint()OpenID Connect 1.0 提供商配置端点 的配置器。
17 logoutEndpoint()OpenID Connect 1.0 注销端点 的配置器。
18 userInfoEndpoint()OpenID Connect 1.0 用户信息端点 的配置器。
19 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")
			.pushedAuthorizationRequestEndpoint("/oauth2/par")
			.deviceAuthorizationEndpoint("/oauth2/device_authorization")
			.deviceVerificationEndpoint("/oauth2/device_verification")
			.tokenEndpoint("/oauth2/token")
			.tokenIntrospectionEndpoint("/oauth2/introspect")
			.tokenRevocationEndpoint("/oauth2/revoke")
			.clientRegistrationEndpoint("/oauth2/register")
			.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")
		.pushedAuthorizationRequestEndpoint("/oauth2/v1/par")
		.deviceAuthorizationEndpoint("/oauth2/v1/device_authorization")
		.deviceVerificationEndpoint("/oauth2/v1/device_verification")
		.tokenEndpoint("/oauth2/v1/token")
		.tokenIntrospectionEndpoint("/oauth2/v1/introspect")
		.tokenRevocationEndpoint("/oauth2/v1/revoke")
		.clientRegistrationEndpoint("/oauth2/v1/register")
		.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 {
	http
		.oauth2AuthorizationServer((authorizationServer) ->
			authorizationServer
				.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()AuthenticationSuccessHandler (后处理器),用于处理成功的客户端认证并将 OAuth2ClientAuthenticationToken 关联到 SecurityContext
6 errorResponseHandler()AuthenticationFailureHandler (后处理器),用于处理失败的客户端认证并返回 OAuth2Error 响应

OAuth2ClientAuthenticationConfigurer 配置 OAuth2ClientAuthenticationFilter 并将其注册到 OAuth2 授权服务器 SecurityFilterChain @BeanOAuth2ClientAuthenticationFilter 是处理客户端认证请求的 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 {
	http
		.oauth2AuthorizationServer((authorizationServer) ->
			authorizationServer
				.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 {
	http
		.oauth2AuthorizationServer((authorizationServer) ->
			authorizationServer
				.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 Key (JWK) Set 检索客户端证书。证书由集合中单个 JWK 的 x5c 参数表示。

客户端证书绑定访问令牌

当在令牌端点使用双向 TLS 客户端认证时,授权服务器能够将颁发的访问令牌绑定到客户端的 X509Certificate。绑定通过计算客户端 X509Certificate 的 SHA-256 拇指纹并将该拇指纹与访问令牌关联来实现。例如,JWT 访问令牌将包含一个 x5t#S256 声明,该声明位于顶级 cnf(确认方法)声明中,其中包含 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();
© . This site is unofficial and not affiliated with VMware.