組態模型
預設組態
OAuth2AuthorizationServerConfiguration
是一個 @Configuration
,為 OAuth2 授權伺服器提供最簡化的預設組態。
OAuth2AuthorizationServerConfiguration
使用 OAuth2AuthorizationServerConfigurer
套用預設組態,並註冊一個 SecurityFilterChain
@Bean
,其中包含所有支援 OAuth2 授權伺服器的基礎架構組件。
OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(HttpSecurity) 是一個便利的 (static ) 輔助方法,可將預設的 OAuth2 安全組態套用至 HttpSecurity 。 |
OAuth2 授權伺服器 SecurityFilterChain
@Bean
配置了以下預設協定端點
JWK 集合端點僅在註冊 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 {
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 用戶端註冊端點 的 REQUIRED(必要) 項目。 |
以下範例示範如何註冊 JwtDecoder
@Bean
@Bean
public JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) {
return OAuth2AuthorizationServerConfiguration.jwtDecoder(jwkSource);
}
OAuth2AuthorizationServerConfiguration
的主要目的是提供一個便利的方法,為 OAuth2 授權伺服器套用最簡化的預設組態。然而,在大多數情況下,將需要自訂組態。
自訂組態
OAuth2AuthorizationServerConfigurer
提供完整自訂 OAuth2 授權伺服器安全組態的能力。它讓您指定要使用的核心組件 - 例如,RegisteredClientRepository
、OAuth2AuthorizationService
、OAuth2TokenGenerator
等。此外,它還讓您自訂協定端點的請求處理邏輯 – 例如,授權端點、裝置授權端點、裝置驗證端點、令牌端點、令牌內省端點 等。
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 (REQUIRED)。 |
2 | authorizationService() :用於管理新的和現有授權的 OAuth2AuthorizationService 。 |
3 | authorizationConsentService() :用於管理新的和現有授權同意的 OAuth2AuthorizationConsentService 。 |
4 | authorizationServerSettings() :用於自訂 OAuth2 授權伺服器組態設定的 AuthorizationServerSettings (REQUIRED)。 |
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 是 REQUIRED(必要) 組件。 |
@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 List 的存取權,允許新增、移除或自訂特定的 AuthenticationConverter 。 |
3 | authenticationProvider() :新增一個 AuthenticationProvider (主要處理器),用於驗證 OAuth2ClientAuthenticationToken 。 |
4 | authenticationProviders() :設定 Consumer ,提供對預設和(可選)新增的 AuthenticationProvider List 的存取權,允許新增、移除或自訂特定的 AuthenticationProvider 。 |
5 | authenticationSuccessHandler() :AuthenticationSuccessHandler (後處理器),用於處理成功的用戶端驗證,並將 OAuth2ClientAuthenticationToken 與 SecurityContext 關聯。 |
6 | errorResponseHandler() :AuthenticationFailureHandler (後處理器),用於處理失敗的用戶端驗證,並傳回 OAuth2Error 回應。 |
OAuth2ClientAuthenticationConfigurer
配置 OAuth2ClientAuthenticationFilter
,並將其註冊到 OAuth2 授權伺服器 SecurityFilterChain
@Bean
。OAuth2ClientAuthenticationFilter
是處理用戶端驗證請求的 Filter
。
預設情況下,OAuth2 令牌端點、OAuth2 令牌內省端點 和 OAuth2 令牌撤銷端點 需要用戶端驗證。支援的用戶端驗證方法包括 client_secret_basic
、client_secret_post
、private_key_jwt
、client_secret_jwt
、tls_client_auth
、self_signed_tls_client_auth
和 none
(公開用戶端)。
OAuth2ClientAuthenticationFilter
配置了以下預設值
-
AuthenticationConverter
— 由JwtClientAssertionAuthenticationConverter
、X509ClientCertificateAuthenticationConverter
、ClientSecretBasicAuthenticationConverter
、ClientSecretPostAuthenticationConverter
和PublicClientAuthenticationConverter
組成的DelegatingAuthenticationConverter
。 -
AuthenticationManager
— 由JwtClientAssertionAuthenticationProvider
、X509ClientCertificateAuthenticationProvider
、ClientSecretAuthenticationProvider
和PublicClientAuthenticationProvider
組成的AuthenticationManager
。 -
AuthenticationSuccessHandler
— 將「已驗證」的OAuth2ClientAuthenticationToken
(目前Authentication
)與SecurityContext
關聯的內部實作。 -
AuthenticationFailureHandler
— 使用與OAuth2AuthenticationException
關聯的OAuth2Error
來傳回 OAuth2 錯誤回應的內部實作。
自訂 Jwt 用戶端斷言驗證
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY
是預設工廠,為指定的 RegisteredClient
提供 OAuth2TokenValidator<Jwt>
,並用於驗證 Jwt 用戶端斷言的 iss
、sub
、aud
、exp
和 nbf
宣告。
JwtClientAssertionDecoderFactory
提供覆寫預設 Jwt 用戶端斷言驗證的能力,方法是提供 Function<RegisteredClient, OAuth2TokenValidator<Jwt>>
類型的自訂工廠給 setJwtValidatorFactory()
。
JwtClientAssertionDecoderFactory 是 JwtClientAssertionAuthenticationProvider 使用的預設 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);
}
});
}
自訂 Mutual-TLS 用戶端驗證
當在 OAuth2 用戶端驗證期間使用 ClientAuthenticationMethod.TLS_CLIENT_AUTH
或 ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
方法時,X509ClientCertificateAuthenticationProvider
用於驗證接收到的用戶端 X509Certificate
鏈。它也與 "Certificate Verifier" 組成,用於在 TLS 握手成功完成後驗證用戶端 X509Certificate
的內容。
PKI Mutual-TLS 方法
對於 PKI Mutual-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);
}
});
}
自簽憑證 Mutual-TLS 方法
對於自簽憑證 Mutual-TLS (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH
) 方法,憑證驗證器的預設實作將使用設定 RegisteredClient.getClientSettings.getJwkSetUrl()
檢索用戶端的 JSON Web Key 集合,並期望找到與在 TLS 握手期間接收到的用戶端 X509Certificate
的匹配項。
RegisteredClient.getClientSettings.getJwkSetUrl() 設定用於透過 JSON Web Key (JWK) 集合檢索用戶端的憑證。憑證以集合中個別 JWK 的 x5c 參數表示。 |
用戶端憑證繫結的存取令牌
當在令牌端點使用 Mutual-TLS 用戶端驗證時,授權伺服器能夠將發行的存取令牌繫結到用戶端的 X509Certificate
。繫結是透過計算用戶端 X509Certificate
的 SHA-256 指紋,並將指紋與存取令牌關聯來完成。例如,JWT 存取令牌將在頂層 cnf
(確認方法)宣告中包含 x5t#S256
宣告,其中包含 X509Certificate
指紋。
將存取令牌繫結到用戶端的 X509Certificate
提供了在受保護資源存取期間實作持有證明機制的可能性。例如,受保護資源將取得在 Mutual-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();