配置模型

默认配置

OAuth2AuthorizationServerConfiguration 是一个 @Configuration,为 OAuth2 授权服务器提供最小化的默认配置。spring-doc.cadn.net.cn

OAuth2AuthorizationServerConfiguration 使用 OAuth2AuthorizationServerConfigurer 应用默认配置,并注册一个由支持 OAuth2 授权服务器的所有基础组件组成的 SecurityFilterChain @Beanspring-doc.cadn.net.cn

OAuth2 授权服务器的 SecurityFilterChain @Bean 使用以下默认协议端点进行配置:spring-doc.cadn.net.cn

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

以下示例展示了如何使用 OAuth2AuthorizationServerConfiguration 应用最小化的默认配置:spring-doc.cadn.net.cn

@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 授权要求资源所有者必须经过身份认证。因此,除了默认的 OAuth2 安全配置之外,还必须配置一种用户身份认证机制。

OpenID Connect 1.0 在默认配置中是禁用的。以下示例展示了如何通过初始化 OidcConfigurer 来启用 OpenID Connect 1.0:spring-doc.cadn.net.cn

@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 协议端点:spring-doc.cadn.net.cn

OpenID Connect 1.0 客户端注册端点 默认处于禁用状态。
OAuth2AuthorizationServerConfiguration.jwtDecoder(JWKSource<SecurityContext>) 是一个便捷的(static)工具方法,可用于注册一个 JwtDecoder @Bean,该 Bean 对于 OpenID Connect 1.0 UserInfo 端点OpenID Connect 1.0 客户端注册端点必需的

以下示例展示了如何注册一个 JwtDecoder @Beanspring-doc.cadn.net.cn

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

OAuth2AuthorizationServerConfiguration 的主要目的是提供一种便捷的方法,用于应用 OAuth2 授权服务器所需的最小默认配置。然而,在大多数情况下,都需要对配置进行自定义。spring-doc.cadn.net.cn

自定义配置

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

OAuth2AuthorizationServerConfigurer 提供以下配置选项:spring-doc.cadn.net.cn

@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 授权服务器支持的Tokens的 OAuth2TokenGenerator
6 clientAuthentication():用于OAuth2 客户端认证的配置器。
7 authorizationEndpoint():用于配置OAuth2 授权端点的配置器。
8 pushedAuthorizationRequestEndpoint():用于配置OAuth2 推送授权请求端点的配置器。
9 deviceAuthorizationEndpoint():用于配置OAuth2 设备授权端点的配置器。
10 deviceVerificationEndpoint():用于配置OAuth2 设备验证端点的配置器。
11 tokenEndpoint():用于配置OAuth2 Tokens端点的配置器。
12 tokenIntrospectionEndpoint():用于配置OAuth2 Tokens自省端点的配置器。
13 tokenRevocationEndpoint():用于配置OAuth2 Tokens撤销端点的配置器。
14 clientRegistrationEndpoint():用于OAuth2 客户端注册端点的配置器。
15 authorizationServerMetadataEndpoint():用于配置OAuth2 授权服务器元数据端点的配置器。
16 providerConfigurationEndpoint():用于配置OpenID Connect 1.0 提供者配置端点的配置器。
17 logoutEndpoint():用于配置OpenID Connect 1.0 注销端点的配置器。
18 userInfoEndpoint():用于配置OpenID Connect 1.0 UserInfo 端点的配置器。
19 clientRegistrationEndpoint():用于配置OpenID Connect 1.0 客户端注册端点的配置器。

配置授权服务器设置

AuthorizationServerSettings 包含 OAuth2 授权服务器的配置设置。 它指定了协议端点的 URI 以及颁发者标识符。 协议端点的默认 URI 如下:spring-doc.cadn.net.cn

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 @Beanspring-doc.cadn.net.cn

@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 和“当前”颁发者标识符的访问。spring-doc.cadn.net.cn

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

配置客户端认证

OAuth2ClientAuthenticationConfigurer 提供了自定义OAuth2 客户端认证的能力。 它定义了扩展点,允许你自定义客户端认证请求的预处理、主处理和后处理逻辑。spring-doc.cadn.net.cn

OAuth2ClientAuthenticationConfigurer 提供以下配置选项:spring-doc.cadn.net.cn

@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,用于访问默认的(以及可选添加的)List 列表,从而允许添加、删除或自定义特定的 AuthenticationConverter
3 authenticationProvider():添加一个用于对 AuthenticationProvider 进行身份验证的 OAuth2ClientAuthenticationToken主处理器)。
4 authenticationProviders():设置一个 Consumer,用于访问默认的(以及可选添加的)List 列表,从而允许添加、删除或自定义特定的 AuthenticationProvider
5 authenticationSuccessHandler():用于处理客户端成功认证并将 AuthenticationSuccessHandler 关联到 OAuth2ClientAuthenticationTokenSecurityContext后处理器)。
6 errorResponseHandler(): 用于处理客户端认证失败并返回 OAuth2Error 响应AuthenticationFailureHandler后置处理器)。

OAuth2ClientAuthenticationConfigurer 用于配置 OAuth2ClientAuthenticationFilter,并将其注册到 OAuth2 授权服务器的 SecurityFilterChain @Bean 中。 OAuth2ClientAuthenticationFilter 是用于处理客户端身份验证请求的 Filterspring-doc.cadn.net.cn

默认情况下,OAuth2 Tokens端点OAuth2 Tokens内省端点OAuth2 Tokens撤销端点需要客户端身份验证。 支持的客户端身份验证方法包括 client_secret_basicclient_secret_postprivate_key_jwtclient_secret_jwttls_client_authself_signed_tls_client_authnone(公共客户端)。spring-doc.cadn.net.cn

OAuth2ClientAuthenticationFilter 的默认配置如下:spring-doc.cadn.net.cn

  • AuthenticationConverter — 一个由 DelegatingAuthenticationConverterJwtClientAssertionAuthenticationConverterX509ClientCertificateAuthenticationConverterClientSecretBasicAuthenticationConverterClientSecretPostAuthenticationConverter 组成的 PublicClientAuthenticationConverterspring-doc.cadn.net.cn

  • AuthenticationManager — 一个由 AuthenticationManagerJwtClientAssertionAuthenticationProviderX509ClientCertificateAuthenticationProviderClientSecretAuthenticationProvider 组成的 PublicClientAuthenticationProviderspring-doc.cadn.net.cn

  • AuthenticationSuccessHandler — 一个内部实现,用于将“已认证”的 OAuth2ClientAuthenticationToken(当前的 Authentication)关联到 SecurityContextspring-doc.cadn.net.cn

  • AuthenticationFailureHandler — 一个内部实现,它使用与 OAuth2Error 关联的 OAuth2AuthenticationException 来返回 OAuth2 错误响应。spring-doc.cadn.net.cn

自定义 Jwt 客户端断言验证

JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY 是默认工厂,它为指定的 RegisteredClient 提供 OAuth2TokenValidator<Jwt>,并用于验证 Jwt 客户端断言的 isssubaudexpnbf 声明。spring-doc.cadn.net.cn

JwtClientAssertionDecoderFactory 提供了通过向 Jwt 方法传入一个类型为 Function<RegisteredClient, OAuth2TokenValidator<Jwt>> 的自定义工厂,来覆盖默认的 setJwtValidatorFactory() 客户端断言验证的能力。spring-doc.cadn.net.cn

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

自定义 JwtClientAssertionDecoderFactory 的一个常见用例是在 Jwt 客户端断言中验证额外的声明(claims)。spring-doc.cadn.net.cn

以下示例展示了如何使用一个自定义的 JwtClientAssertionAuthenticationProvider 来配置 JwtClientAssertionDecoderFactory,该工厂会在 Jwt 客户端断言中验证一个额外的声明(claim):spring-doc.cadn.net.cn

@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 客户端认证过程中,当使用 X509CertificateClientAuthenticationMethod.TLS_CLIENT_AUTH 方法时,对收到的客户端 ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH 证书链进行认证。 它还包含一个“证书验证器”(Certificate Verifier),用于在 TLS 握手成功完成后验证客户端 X509Certificate 的内容。spring-doc.cadn.net.cn

PKI 双向 TLS 方法

对于 PKI 双向 TLS(ClientAuthenticationMethod.TLS_CLIENT_AUTH)方法,默认的证书验证器实现会将客户端 X509Certificate 的主题可分辨名称(Subject Distinguished Name)与配置项 RegisteredClient.getClientSettings.getX509CertificateSubjectDN() 进行比对验证。spring-doc.cadn.net.cn

如果你需要验证客户端 X509Certificate 的另一个属性,例如主题备用名称(Subject Alternative Name,SAN)条目,下面的示例展示了如何使用自定义的证书验证器实现来配置 X509ClientCertificateAuthenticationProviderspring-doc.cadn.net.cn

@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 密钥集(JWK Set),并期望在 TLS 握手过程中收到的客户端 X509Certificate 与该密钥集中的一项相匹配。spring-doc.cadn.net.cn

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

客户端证书绑定访问Tokens

当在Tokens端点使用双向 TLS(Mutual-TLS)客户端认证时,授权服务器能够将所颁发的访问Tokens绑定到客户端的 X509Certificate。 这种绑定通过计算客户端 X509Certificate 的 SHA-256 指纹,并将该指纹与访问Tokens关联来实现。 例如,JWT 格式的访问Tokens将在顶层的 x5t#S256(确认方法)声明中包含一个 X509Certificate 声明,其中包含 cnf 的指纹。spring-doc.cadn.net.cn

将访问Tokens绑定到客户端的X509Certificate,可在受保护资源访问期间实现一种持有证明(proof-of-possession)机制。 例如,受保护资源会获取客户端在双向 TLS(Mutual-TLS)认证过程中使用的X509Certificate,然后验证该证书的指纹是否与访问Tokens中关联的x5t#S256声明相匹配。spring-doc.cadn.net.cn

以下示例展示了如何为客户端启用证书绑定访问Tokens:spring-doc.cadn.net.cn

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