配置模型

預設配置

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

}
authorization_code 授權要求資源所有者經過身份驗證。因此,除了預設的 OAuth2 安全配置外,必須配置使用者身份驗證機制。

OpenID Connect 1.0 在預設配置中是停用的。以下示例展示瞭如何透過初始化 OidcConfigurer 來啟用 OpenID Connect 1.0

@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
	OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
			OAuth2AuthorizationServerConfigurer.authorizationServer();
	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (authorizationServer) ->
			authorizationServer
				.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,該 @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 等。此外,它允許您定製協議端點的請求處理邏輯——例如,授權端點裝置授權端點裝置驗證端點Token 端點Token 內省端點 等。

OAuth2AuthorizationServerConfigurer 提供了以下配置選項

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

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (authorizationServer) ->
			authorizationServer
				.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 授權伺服器支援的 Token 的 OAuth2TokenGenerator
6 clientAuthentication()OAuth2 客戶端身份驗證的配置器。
7 authorizationEndpoint()OAuth2 授權端點的配置器。
8 deviceAuthorizationEndpoint()OAuth2 裝置授權端點的配置器。
9 deviceVerificationEndpoint()OAuth2 裝置驗證端點的配置器。
10 tokenEndpoint()OAuth2 Token 端點的配置器。
11 tokenIntrospectionEndpoint()OAuth2 Token 內省端點的配置器。
12 tokenRevocationEndpoint()OAuth2 Token 撤銷端點的配置器。
13 authorizationServerMetadataEndpoint()OAuth2 授權伺服器元資料端點的配置器。
14 providerConfigurationEndpoint()OpenID Connect 1.0 Provider 配置端點的配置器。
15 logoutEndpoint()OpenID Connect 1.0 Logout 端點的配置器。
16 userInfoEndpoint()OpenID Connect 1.0 UserInfo 端點的配置器。
17 clientRegistrationEndpoint()OpenID Connect 1.0 客戶端註冊端點的配置器。

配置授權伺服器設定

AuthorizationServerSettings 包含 OAuth2 授權伺服器的配置設定。它指定協議端點的 URI 以及 Issuer Identifier。協議端點的預設 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 和“當前”Issuer Identifier 的訪問。

如果 Issuer Identifier 未在 AuthorizationServerSettings.builder().issuer(String) 中配置,則會從當前請求中解析。
透過 AuthorizationServerContextHolder 可以訪問 AuthorizationServerContext,後者使用 ThreadLocal 將其與當前請求執行緒關聯。

配置客戶端身份驗證

OAuth2ClientAuthenticationConfigurer 提供了定製 OAuth2 客戶端身份驗證的能力。它定義了擴充套件點,允許您定製客戶端身份驗證請求的預處理、主要處理和後處理邏輯。

OAuth2ClientAuthenticationConfigurer 提供了以下配置選項

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

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (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():用於處理成功客戶端身份驗證並將 OAuth2ClientAuthenticationToken 關聯到 SecurityContextAuthenticationSuccessHandler後處理器)。
6 errorResponseHandler():用於處理失敗客戶端身份驗證並返回 OAuth2Error 響應AuthenticationFailureHandler後處理器)。

OAuth2ClientAuthenticationConfigurer 配置 OAuth2ClientAuthenticationFilter 並將其註冊到 OAuth2 授權伺服器 SecurityFilterChain @BeanOAuth2ClientAuthenticationFilter 是處理客戶端身份驗證請求的 Filter

預設情況下,OAuth2 Token 端點、OAuth2 Token 內省端點和 OAuth2 Token 撤銷端點需要客戶端身份驗證。支援的客戶端身份驗證方法有 client_secret_basicclient_secret_postprivate_key_jwtclient_secret_jwttls_client_authself_signed_tls_client_authnone(公共客戶端)。

OAuth2ClientAuthenticationFilter 配置了以下預設設定

  • AuthenticationConverter — 一個 DelegatingAuthenticationConverter,由 JwtClientAssertionAuthenticationConverterX509ClientCertificateAuthenticationConverterClientSecretBasicAuthenticationConverterClientSecretPostAuthenticationConverterPublicClientAuthenticationConverter 組成。

  • AuthenticationManager — 一個 AuthenticationManager,由 JwtClientAssertionAuthenticationProviderX509ClientCertificateAuthenticationProviderClientSecretAuthenticationProviderPublicClientAuthenticationProvider 組成。

  • AuthenticationSuccessHandler — 一個內部實現,用於將“已認證的” OAuth2ClientAuthenticationToken(當前 Authentication)關聯到 SecurityContext

  • AuthenticationFailureHandler — 一個內部實現,使用與 OAuth2AuthenticationException 關聯的 OAuth2Error 返回 OAuth2 錯誤響應。

定製 Jwt Client Assertion 驗證

JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY 是預設的工廠,它為指定的 RegisteredClient 提供一個 OAuth2TokenValidator<Jwt>,用於驗證 Jwt client assertion 的 isssubaudexpnbf Claims。

JwtClientAssertionDecoderFactory 提供了透過向 setJwtValidatorFactory() 提供型別為 Function<RegisteredClient, OAuth2TokenValidator<Jwt>> 的自定義工廠來覆蓋預設 Jwt client assertion 驗證的能力。

JwtClientAssertionDecoderFactoryJwtClientAssertionAuthenticationProvider 使用的預設 JwtDecoderFactory,它為指定的 RegisteredClient 提供一個 JwtDecoder,用於在 OAuth2 客戶端身份驗證期間驗證 Jwt 持有者令牌。

定製 JwtClientAssertionDecoderFactory 的一個常見用例是在 Jwt client assertion 中驗證額外的 Claims。

以下示例展示瞭如何使用定製的 JwtClientAssertionDecoderFactory 配置 JwtClientAssertionAuthenticationProvider,該工廠驗證 Jwt client assertion 中的一個附加 Claim

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

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (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);
			}
		});
}

定製 Mutual-TLS 客戶端身份驗證

X509ClientCertificateAuthenticationProvider 用於驗證在 OAuth2 客戶端身份驗證期間使用 ClientAuthenticationMethod.TLS_CLIENT_AUTHClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH 方法時接收到的客戶端 X509Certificate 鏈。它還包含一個“證書驗證器”,用於在 TLS 握手成功完成後驗證客戶端 X509Certificate 的內容。

PKI Mutual-TLS 方法

對於 PKI Mutual-TLS(ClientAuthenticationMethod.TLS_CLIENT_AUTH)方法,證書驗證器的預設實現根據設定 RegisteredClient.getClientSettings.getX509CertificateSubjectDN() 驗證客戶端 X509Certificate 的主題可分辨名稱。

如果您需要驗證客戶端 X509Certificate 的其他屬性,例如 Subject Alternative Name (SAN) 條目,以下示例展示瞭如何使用證書驗證器的自定義實現配置 X509ClientCertificateAuthenticationProvider

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

	http
		.securityMatcher(authorizationServerConfigurer.getEndpointsMatcher())
		.with(authorizationServerConfigurer, (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);
				}
			});
}

自簽名證書 Mutual-TLS 方法

對於自簽名證書 Mutual-TLS(ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH)方法,證書驗證器的預設實現將使用設定 RegisteredClient.getClientSettings.getJwkSetUrl() 檢索客戶端的 JSON Web Key Set,並期望找到與 TLS 握手期間接收到的客戶端 X509Certificate 匹配的項。

RegisteredClient.getClientSettings.getJwkSetUrl() 設定用於透過 JSON Web Key (JWK) Set 檢索客戶端的證書。證書由集合中單個 JWK 的 x5c 引數表示。

客戶端證書繫結的訪問令牌

當在 Token 端點使用 Mutual-TLS 客戶端身份驗證時,授權伺服器能夠將頒發的訪問令牌繫結到客戶端的 X509Certificate。繫結是透過計算客戶端 X509Certificate 的 SHA-256 指紋,並將指紋與訪問令牌關聯來完成的。例如,一個 JWT 訪問令牌會在頂級 cnf(confirmation method)Claim 中包含一個 x5t#S256 Claim,其中包含 X509Certificate 指紋。

將訪問令牌繫結到客戶端的 X509Certificate 提供了在訪問受保護資源時實現持有者證明機制的能力。例如,受保護資源會獲取在 Mutual-TLS 身份驗證期間使用的客戶端 X509Certificate,然後驗證證書指紋是否與訪問令牌關聯的 x5t#S256 Claim 匹配。

以下示例展示瞭如何為客戶端啟用證書繫結的訪問令牌

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