配置模型
預設配置
OAuth2AuthorizationServerConfiguration 是一個 @Configuration,它為 OAuth2 授權伺服器提供了最小的預設配置。
OAuth2AuthorizationServerConfiguration 使用 OAuth2AuthorizationServerConfigurer 來應用預設配置,並註冊一個由所有支援 OAuth2 授權伺服器的基礎設施元件組成的 SecurityFilterChain @Bean。
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 {
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,這對於 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 授權伺服器完全自定義安全配置的能力。它允許您指定要使用的核心元件——例如,RegisteredClientRepository、OAuth2AuthorizationService、OAuth2TokenGenerator 等。此外,它還允許您自定義協議端點的請求處理邏輯——例如,授權端點、裝置授權端點、裝置驗證端點、令牌端點、令牌內省端點 等。
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)
.pushedAuthorizationRequestEndpoint(pushedAuthorizationRequestEndpoint -> { }) (8)
.deviceAuthorizationEndpoint(deviceAuthorizationEndpoint -> { }) (9)
.deviceVerificationEndpoint(deviceVerificationEndpoint -> { }) (10)
.tokenEndpoint(tokenEndpoint -> { }) (11)
.tokenIntrospectionEndpoint(tokenIntrospectionEndpoint -> { }) (12)
.tokenRevocationEndpoint(tokenRevocationEndpoint -> { }) (13)
.authorizationServerMetadataEndpoint(authorizationServerMetadataEndpoint -> { }) (14)
.oidc(oidc -> oidc
.providerConfigurationEndpoint(providerConfigurationEndpoint -> { }) (15)
.logoutEndpoint(logoutEndpoint -> { }) (16)
.userInfoEndpoint(userInfoEndpoint -> { }) (17)
.clientRegistrationEndpoint(clientRegistrationEndpoint -> { }) (18)
)
);
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 | authorizationServerMetadataEndpoint():OAuth2 授權伺服器元資料端點的配置器。 |
| 15 | providerConfigurationEndpoint():OpenID Connect 1.0 提供商配置端點的配置器。 |
| 16 | logoutEndpoint():OpenID Connect 1.0 登出端點的配置器。 |
| 17 | userInfoEndpoint():OpenID Connect 1.0 使用者資訊端點的配置器。 |
| 18 | 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")
.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")
.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 =
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():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— 一個DelegatingAuthenticationConverter,由JwtClientAssertionAuthenticationConverter、X509ClientCertificateAuthenticationConverter、ClientSecretBasicAuthenticationConverter、ClientSecretPostAuthenticationConverter和PublicClientAuthenticationConverter組成。 -
AuthenticationManager— 一個AuthenticationManager,由JwtClientAssertionAuthenticationProvider、X509ClientCertificateAuthenticationProvider、ClientSecretAuthenticationProvider和PublicClientAuthenticationProvider組成。 -
AuthenticationSuccessHandler— 一個內部實現,將“已認證”的OAuth2ClientAuthenticationToken(當前Authentication)關聯到SecurityContext。 -
AuthenticationFailureHandler— 一個內部實現,使用與OAuth2AuthenticationException關聯的OAuth2Error返回 OAuth2 錯誤響應。
自定義 Jwt 客戶端斷言驗證
JwtClientAssertionDecoderFactory.DEFAULT_JWT_VALIDATOR_FACTORY 是預設的工廠,它為指定的 RegisteredClient 提供一個 OAuth2TokenValidator<Jwt>,用於驗證 Jwt 客戶端斷言的 iss、sub、aud、exp 和 nbf 宣告。
JwtClientAssertionDecoderFactory 提供了透過向 setJwtValidatorFactory() 提供型別為 Function<RegisteredClient, OAuth2TokenValidator<Jwt>> 的自定義工廠來覆蓋預設 Jwt 客戶端斷言驗證的能力。
JwtClientAssertionDecoderFactory 是 JwtClientAssertionAuthenticationProvider 使用的預設 JwtDecoderFactory,它為指定的 RegisteredClient 提供一個 JwtDecoder,用於在 OAuth2 客戶端身份驗證期間驗證 Jwt 持有者令牌。 |
自定義 JwtClientAssertionDecoderFactory 的常見用例是驗證 Jwt 客戶端斷言中的其他宣告。
以下示例展示瞭如何使用自定義的 JwtClientAssertionDecoderFactory 配置 JwtClientAssertionAuthenticationProvider,該工廠驗證 Jwt 客戶端斷言中的附加宣告:
@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);
}
});
}
自定義雙向 TLS 客戶端身份驗證
X509ClientCertificateAuthenticationProvider 用於在 OAuth2 客戶端身份驗證期間使用 ClientAuthenticationMethod.TLS_CLIENT_AUTH 或 ClientAuthenticationMethod.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 {
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);
}
});
}
自簽名證書雙向 TLS 方法
對於自簽名證書雙向 TLS (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH) 方法,證書驗證器的預設實現將使用設定 RegisteredClient.getClientSettings.getJwkSetUrl() 檢索客戶端的 JSON Web 金鑰集,並期望找到與 TLS 握手期間收到的客戶端 X509Certificate 匹配的項。
RegisteredClient.getClientSettings.getJwkSetUrl() 設定用於透過 JSON Web 金鑰 (JWK) 集檢索客戶端的證書。證書由集中單個 JWK 的 x5c 引數表示。 |
客戶端證書繫結訪問令牌
當在令牌端點使用雙向 TLS 客戶端身份驗證時,授權伺服器能夠將頒發的訪問令牌繫結到客戶端的 X509Certificate。繫結是透過計算客戶端 X509Certificate 的 SHA-256 指紋並將該指紋與訪問令牌關聯來完成的。例如,一個 JWT 訪問令牌將包含一個 x5t#S256 宣告,其中包含 X509Certificate 指紋,位於頂級 cnf(確認方法)宣告中。
將訪問令牌繫結到客戶端的 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();