配置模型

預設配置

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.