核心模型 / 組件
RegisteredClient
RegisteredClient
是在授權伺服器上註冊的用戶端表示法。用戶端必須先在授權伺服器上註冊,才能啟動授權 Grant Flow,例如 authorization_code
或 client_credentials
。
在用戶端註冊期間,會為用戶端分配一個唯一的用戶端識別符,(可選)用戶端密碼(取決於用戶端類型),以及與其唯一用戶端識別符相關聯的元數據。用戶端的元數據範圍可以從面向人類的顯示字串(例如用戶端名稱)到特定於協定流程的項目(例如有效重新導向 URI 的列表)。
Spring Security 的 OAuth2 Client 支援中對應的用戶端註冊模型是 ClientRegistration。 |
用戶端的主要目的是請求存取受保護的資源。用戶端首先通過向授權伺服器驗證身份並提交授權 Grant 來請求存取令牌。授權伺服器驗證用戶端和授權 Grant,如果它們有效,則發放存取令牌。用戶端現在可以通過提交存取令牌向資源伺服器請求受保護的資源。
以下範例示範如何配置允許執行 authorization_code Grant Flow 以請求存取令牌的 RegisteredClient
RegisteredClient registeredClient = RegisteredClient.withId(UUID.randomUUID().toString())
.clientId("client-a")
.clientSecret("{noop}secret") (1)
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.redirectUri("http://127.0.0.1:8080/authorized")
.scope("scope-a")
.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build())
.build();
1 | {noop} 代表 Spring Security NoOpPasswordEncoder 的 PasswordEncoder ID。 |
Spring Security OAuth2 Client 支援中的對應組態為
spring:
security:
oauth2:
client:
registration:
client-a:
provider: spring
client-id: client-a
client-secret: secret
authorization-grant-type: authorization_code
redirect-uri: "http://127.0.0.1:8080/authorized"
scope: scope-a
provider:
spring:
issuer-uri: https://127.0.0.1:9000
RegisteredClient
具有與其唯一用戶端識別符相關聯的元數據(屬性),並定義如下
public class RegisteredClient implements Serializable {
private String id; (1)
private String clientId; (2)
private Instant clientIdIssuedAt; (3)
private String clientSecret; (4)
private Instant clientSecretExpiresAt; (5)
private String clientName; (6)
private Set<ClientAuthenticationMethod> clientAuthenticationMethods; (7)
private Set<AuthorizationGrantType> authorizationGrantTypes; (8)
private Set<String> redirectUris; (9)
private Set<String> postLogoutRedirectUris; (10)
private Set<String> scopes; (11)
private ClientSettings clientSettings; (12)
private TokenSettings tokenSettings; (13)
...
}
1 | id :唯一識別 RegisteredClient 的 ID。 |
2 | clientId :用戶端識別符。 |
3 | clientIdIssuedAt :發行用戶端識別符的時間。 |
4 | clientSecret :用戶端的密碼。該值應使用 Spring Security 的 PasswordEncoder 進行編碼。 |
5 | clientSecretExpiresAt :用戶端密碼過期的時間。 |
6 | clientName :用於用戶端的描述性名稱。該名稱可能在某些情況下使用,例如在同意頁面中顯示用戶端名稱時。 |
7 | clientAuthenticationMethods :用戶端可能使用的身份驗證方法。支援的值為 client_secret_basic 、client_secret_post 、private_key_jwt 、client_secret_jwt 和 none (公共用戶端)。 |
8 | authorizationGrantTypes :用戶端可以使用的授權 Grant Type。支援的值為 authorization_code 、client_credentials 、refresh_token 、urn:ietf:params:oauth:grant-type:device_code 和 urn:ietf:params:oauth:grant-type:token-exchange 。 |
9 | redirectUris :註冊的 重新導向 URI,用戶端可以在基於重新導向的流程中使用它們 - 例如,authorization_code Grant。 |
10 | postLogoutRedirectUris :用戶端可以用於登出的登出後重新導向 URI。 |
11 | scopes :允許用戶端請求的 scope。 |
12 | clientSettings :用戶端的自訂設定 - 例如,需要 PKCE、需要授權同意等等。 |
13 | tokenSettings :發放給用戶端的 OAuth2 令牌的自訂設定 - 例如,存取/刷新令牌的存活時間、重複使用刷新令牌等等。 |
RegisteredClientRepository
RegisteredClientRepository
是中心組件,可在其中註冊新用戶端並查詢現有用戶端。其他組件在遵循特定協定流程時使用它,例如用戶端身份驗證、授權 Grant 處理、令牌內省、動態用戶端註冊等等。
提供的 RegisteredClientRepository
實作是 InMemoryRegisteredClientRepository
和 JdbcRegisteredClientRepository
。InMemoryRegisteredClientRepository
實作將 RegisteredClient
實例儲存在記憶體中,並且僅建議在開發和測試期間使用。JdbcRegisteredClientRepository
是 JDBC 實作,它通過使用 JdbcOperations
持久化 RegisteredClient
實例。
RegisteredClientRepository 是必要組件。 |
以下範例示範如何註冊 RegisteredClientRepository
@Bean
@Bean
public RegisteredClientRepository registeredClientRepository() {
List<RegisteredClient> registrations = ...
return new InMemoryRegisteredClientRepository(registrations);
}
或者,您可以通過 OAuth2AuthorizationServerConfigurer
配置 RegisteredClientRepository
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.registeredClientRepository(registeredClientRepository);
...
return http.build();
}
當同時應用多個組態選項時,OAuth2AuthorizationServerConfigurer 非常有用。 |
OAuth2Authorization
OAuth2Authorization
是 OAuth2 授權的表示法,它保存與資源擁有者或自身(在 client_credentials
授權 Grant Type 的情況下)授予用戶端的授權相關的狀態。
Spring Security 的 OAuth2 Client 支援中對應的授權模型是 OAuth2AuthorizedClient。 |
在成功完成授權 Grant Flow 後,將創建 OAuth2Authorization
並將 OAuth2AccessToken
、一個(可選)OAuth2RefreshToken
以及特定於已執行授權 Grant Type 的其他狀態相關聯。
與 OAuth2Authorization
相關聯的 OAuth2Token
實例因授權 Grant Type 而異。
對於 OAuth2 authorization_code Grant,關聯了 OAuth2AuthorizationCode
、OAuth2AccessToken
和(可選)OAuth2RefreshToken
。
對於 OpenID Connect 1.0 authorization_code Grant,關聯了 OAuth2AuthorizationCode
、OidcIdToken
、OAuth2AccessToken
和(可選)OAuth2RefreshToken
。
對於 OAuth2 client_credentials Grant,僅關聯了 OAuth2AccessToken
。
OAuth2Authorization
及其屬性定義如下
public class OAuth2Authorization implements Serializable {
private String id; (1)
private String registeredClientId; (2)
private String principalName; (3)
private AuthorizationGrantType authorizationGrantType; (4)
private Set<String> authorizedScopes; (5)
private Map<Class<? extends OAuth2Token>, Token<?>> tokens; (6)
private Map<String, Object> attributes; (7)
...
}
1 | id :唯一識別 OAuth2Authorization 的 ID。 |
2 | registeredClientId :唯一識別 RegisteredClient 的 ID。 |
3 | principalName :資源擁有者(或用戶端)的主體名稱。 |
4 | authorizationGrantType :使用的 AuthorizationGrantType 。 |
5 | authorizedScopes :為用戶端授權的 scope 的 Set 。 |
6 | tokens :特定於已執行授權 Grant Type 的 OAuth2Token 實例(和相關元數據)。 |
7 | attributes :特定於已執行授權 Grant Type 的其他屬性 - 例如,已驗證的 Principal 、OAuth2AuthorizationRequest 等等。 |
OAuth2Authorization
及其關聯的 OAuth2Token
實例具有設定的生命週期。新發行的 OAuth2Token
是活動的,當它過期或失效(撤銷)時,它會變為非活動狀態。當所有關聯的 OAuth2Token
實例都處於非活動狀態時,OAuth2Authorization
(隱含地)為非活動狀態。每個 OAuth2Token
都保存在 OAuth2Authorization.Token
中,它提供用於 isExpired()
、isInvalidated()
和 isActive()
的存取器。
OAuth2Authorization.Token
還提供 getClaims()
,它返回與 OAuth2Token
關聯的宣告(如果有)。
OAuth2AuthorizationService
OAuth2AuthorizationService
是中心組件,可在其中儲存新的授權並查詢現有的授權。其他組件在遵循特定協定流程時使用它 - 例如,用戶端身份驗證、授權 Grant 處理、令牌內省、令牌撤銷、動態用戶端註冊等等。
提供的 OAuth2AuthorizationService
實作是 InMemoryOAuth2AuthorizationService
和 JdbcOAuth2AuthorizationService
。InMemoryOAuth2AuthorizationService
實作將 OAuth2Authorization
實例儲存在記憶體中,並且僅建議在開發和測試期間使用。JdbcOAuth2AuthorizationService
是 JDBC 實作,它通過使用 JdbcOperations
持久化 OAuth2Authorization
實例。
OAuth2AuthorizationService 是一個可選組件,預設為 InMemoryOAuth2AuthorizationService 。 |
以下範例示範如何註冊 OAuth2AuthorizationService
@Bean
@Bean
public OAuth2AuthorizationService authorizationService() {
return new InMemoryOAuth2AuthorizationService();
}
或者,您可以通過 OAuth2AuthorizationServerConfigurer
配置 OAuth2AuthorizationService
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.authorizationService(authorizationService);
...
return http.build();
}
當同時應用多個組態選項時,OAuth2AuthorizationServerConfigurer 非常有用。 |
OAuth2AuthorizationConsent
OAuth2AuthorizationConsent
是來自 OAuth2 授權請求流程的授權「同意」(決策)的表示法 - 例如,authorization_code
Grant,它保存資源擁有者授予用戶端的權限。
在授權存取用戶端時,資源擁有者可能僅授予用戶端請求的權限的子集。典型的用例是 authorization_code
Grant Flow,其中用戶端請求 scope,資源擁有者授予(或拒絕)對請求的 scope 的存取權。
在完成 OAuth2 授權請求流程後,將創建(或更新)OAuth2AuthorizationConsent
,並將授予的權限與用戶端和資源擁有者關聯。
OAuth2AuthorizationConsent
及其屬性定義如下
public final class OAuth2AuthorizationConsent implements Serializable {
private final String registeredClientId; (1)
private final String principalName; (2)
private final Set<GrantedAuthority> authorities; (3)
...
}
1 | registeredClientId :唯一識別 RegisteredClient 的 ID。 |
2 | principalName :資源擁有者的主體名稱。 |
3 | authorities :資源擁有者授予用戶端的權限。權限可以表示 scope、宣告、權限、角色等等。 |
OAuth2AuthorizationConsentService
OAuth2AuthorizationConsentService
是中心組件,可在其中儲存新的授權同意並查詢現有的授權同意。實作 OAuth2 授權請求流程的組件主要使用它 - 例如,authorization_code
Grant。
提供的 OAuth2AuthorizationConsentService
實作是 InMemoryOAuth2AuthorizationConsentService
和 JdbcOAuth2AuthorizationConsentService
。InMemoryOAuth2AuthorizationConsentService
實作將 OAuth2AuthorizationConsent
實例儲存在記憶體中,並且僅建議用於開發和測試。JdbcOAuth2AuthorizationConsentService
是 JDBC 實作,它通過使用 JdbcOperations
持久化 OAuth2AuthorizationConsent
實例。
OAuth2AuthorizationConsentService 是一個可選組件,預設為 InMemoryOAuth2AuthorizationConsentService 。 |
以下範例示範如何註冊 OAuth2AuthorizationConsentService
@Bean
@Bean
public OAuth2AuthorizationConsentService authorizationConsentService() {
return new InMemoryOAuth2AuthorizationConsentService();
}
或者,您可以通過 OAuth2AuthorizationServerConfigurer
配置 OAuth2AuthorizationConsentService
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.authorizationConsentService(authorizationConsentService);
...
return http.build();
}
當同時應用多個組態選項時,OAuth2AuthorizationServerConfigurer 非常有用。 |
OAuth2TokenContext
OAuth2TokenContext
是一個上下文物件,它保存與 OAuth2Token
相關聯的資訊,並由 OAuth2TokenGenerator 和 OAuth2TokenCustomizer 使用。
OAuth2TokenContext
提供以下存取器
public interface OAuth2TokenContext extends Context {
default RegisteredClient getRegisteredClient() ... (1)
default <T extends Authentication> T getPrincipal() ... (2)
default AuthorizationServerContext getAuthorizationServerContext() ... (3)
@Nullable
default OAuth2Authorization getAuthorization() ... (4)
default Set<String> getAuthorizedScopes() ... (5)
default OAuth2TokenType getTokenType() ... (6)
default AuthorizationGrantType getAuthorizationGrantType() ... (7)
default <T extends Authentication> T getAuthorizationGrant() ... (8)
...
}
1 | getRegisteredClient() :與授權 Grant 相關聯的 RegisteredClient。 |
2 | getPrincipal() :資源擁有者(或用戶端)的 Authentication 實例。 |
3 | getAuthorizationServerContext() :保存授權伺服器運行時環境資訊的 AuthorizationServerContext 物件。 |
4 | getAuthorization() :與授權 Grant 相關聯的 OAuth2Authorization。 |
5 | getAuthorizedScopes() :為用戶端授權的 scope。 |
6 | getTokenType() :要生成的 OAuth2TokenType 。支援的值為 code 、access_token 、refresh_token 和 id_token 。 |
7 | getAuthorizationGrantType() :與授權 Grant 相關聯的 AuthorizationGrantType 。 |
8 | getAuthorizationGrant() :由處理授權 Grant 的 AuthenticationProvider 使用的 Authentication 實例。 |
OAuth2TokenGenerator
OAuth2TokenGenerator
負責從提供的 OAuth2TokenContext 中包含的資訊生成 OAuth2Token
。
生成的 OAuth2Token
主要取決於 OAuth2TokenContext
中指定的 OAuth2TokenType
的類型。
例如,當 OAuth2TokenType
的 value
為
-
code
,則生成OAuth2AuthorizationCode
。 -
access_token
,則生成OAuth2AccessToken
。 -
refresh_token
,則生成OAuth2RefreshToken
。 -
id_token
,則生成OidcIdToken
。
此外,生成的 OAuth2AccessToken
的格式因 RegisteredClient 配置的 TokenSettings.getAccessTokenFormat()
而異。如果格式為 OAuth2TokenFormat.SELF_CONTAINED
(預設值),則生成 Jwt
。如果格式為 OAuth2TokenFormat.REFERENCE
,則生成「不透明」令牌。
最後,如果生成的 OAuth2Token
具有一組宣告並實作 ClaimAccessor
,則可以從 OAuth2Authorization.Token.getClaims() 存取宣告。
OAuth2TokenGenerator
主要由實作授權 Grant 處理的組件使用 - 例如,authorization_code
、client_credentials
和 refresh_token
。
提供的實作是 OAuth2AccessTokenGenerator
、OAuth2RefreshTokenGenerator
和 JwtGenerator
。OAuth2AccessTokenGenerator
生成「不透明」(OAuth2TokenFormat.REFERENCE
)存取令牌,而 JwtGenerator
生成 Jwt
(OAuth2TokenFormat.SELF_CONTAINED
)。
OAuth2TokenGenerator 是一個可選組件,預設為由 OAuth2AccessTokenGenerator 和 OAuth2RefreshTokenGenerator 組成的 DelegatingOAuth2TokenGenerator 。 |
如果註冊了 JwtEncoder @Bean 或 JWKSource<SecurityContext> @Bean ,則 JwtGenerator 也會額外組成在 DelegatingOAuth2TokenGenerator 中。 |
OAuth2TokenGenerator
提供了極大的靈活性,因為它可以支援 access_token
和 refresh_token
的任何自訂令牌格式。
以下範例示範如何註冊 OAuth2TokenGenerator
@Bean
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
或者,您可以通過 OAuth2AuthorizationServerConfigurer
配置 OAuth2TokenGenerator
@Bean
public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception {
OAuth2AuthorizationServerConfigurer authorizationServerConfigurer =
new OAuth2AuthorizationServerConfigurer();
http.apply(authorizationServerConfigurer);
authorizationServerConfigurer
.tokenGenerator(tokenGenerator);
...
return http.build();
}
當同時應用多個組態選項時,OAuth2AuthorizationServerConfigurer 非常有用。 |
OAuth2TokenCustomizer
OAuth2TokenCustomizer
提供了自訂 OAuth2Token
屬性的能力,這些屬性可在提供的 OAuth2TokenContext 中存取。它由 OAuth2TokenGenerator 使用,以使其能夠在生成 OAuth2Token
之前自訂其屬性。
使用 OAuth2TokenClaimsContext
(implements OAuth2TokenContext
)的泛型類型宣告的 OAuth2TokenCustomizer<OAuth2TokenClaimsContext>
提供了自訂「不透明」OAuth2AccessToken
的宣告的能力。OAuth2TokenClaimsContext.getClaims()
提供了對 OAuth2TokenClaimsSet.Builder
的存取,允許新增、替換和移除宣告。
以下範例示範如何實作 OAuth2TokenCustomizer<OAuth2TokenClaimsContext>
並使用 OAuth2AccessTokenGenerator
配置它
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer());
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
@Bean
public OAuth2TokenCustomizer<OAuth2TokenClaimsContext> accessTokenCustomizer() {
return context -> {
OAuth2TokenClaimsSet.Builder claims = context.getClaims();
// Customize claims
};
}
如果未將 OAuth2TokenGenerator 作為 @Bean 提供或未通過 OAuth2AuthorizationServerConfigurer 配置,則 OAuth2TokenCustomizer<OAuth2TokenClaimsContext> @Bean 將自動使用 OAuth2AccessTokenGenerator 進行配置。 |
使用 JwtEncodingContext
(implements OAuth2TokenContext
)的泛型類型宣告的 OAuth2TokenCustomizer<JwtEncodingContext>
提供了自訂 Jwt
的標頭和宣告的能力。JwtEncodingContext.getJwsHeader()
提供了對 JwsHeader.Builder
的存取,允許新增、替換和移除標頭。JwtEncodingContext.getClaims()
提供了對 JwtClaimsSet.Builder
的存取,允許新增、替換和移除宣告。
以下範例示範如何實作 OAuth2TokenCustomizer<JwtEncodingContext>
並使用 JwtGenerator
配置它
@Bean
public OAuth2TokenGenerator<?> tokenGenerator() {
JwtEncoder jwtEncoder = ...
JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder);
jwtGenerator.setJwtCustomizer(jwtCustomizer());
OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator();
OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator();
return new DelegatingOAuth2TokenGenerator(
jwtGenerator, accessTokenGenerator, refreshTokenGenerator);
}
@Bean
public OAuth2TokenCustomizer<JwtEncodingContext> jwtCustomizer() {
return context -> {
JwsHeader.Builder headers = context.getJwsHeader();
JwtClaimsSet.Builder claims = context.getClaims();
if (context.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) {
// Customize headers/claims for access_token
} else if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {
// Customize headers/claims for id_token
}
};
}
如果未將 OAuth2TokenGenerator 作為 @Bean 提供或未通過 OAuth2AuthorizationServerConfigurer 配置,則 OAuth2TokenCustomizer<JwtEncodingContext> @Bean 將自動使用 JwtGenerator 進行配置。 |
有關如何自訂 ID 令牌的範例,請參閱指南 操作指南:自訂 OpenID Connect 1.0 UserInfo 回應。 |
SessionRegistry
如果啟用 OpenID Connect 1.0,則會使用 SessionRegistry
實例來追蹤已驗證的會話。SessionRegistry
由與 OAuth2 授權端點關聯的 SessionAuthenticationStrategy
的預設實作使用,以註冊新的已驗證會話。
如果未註冊 SessionRegistry @Bean ,則將使用預設實作 SessionRegistryImpl 。 |
如果註冊了 SessionRegistry @Bean 並且是 SessionRegistryImpl 的實例,則應也註冊 HttpSessionEventPublisher @Bean ,因為它負責通知 SessionRegistryImpl 會話生命週期事件,例如 SessionDestroyedEvent ,以提供移除 SessionInformation 實例的能力。 |
當最終用戶請求登出時,OpenID Connect 1.0 登出端點 使用 SessionRegistry
查找與已驗證的最終用戶關聯的 SessionInformation
以執行登出。
如果正在使用 Spring Security 的 並行會話控制 功能,則建議註冊 SessionRegistry
@Bean
以確保它在 Spring Security 的並行會話控制和 Spring Authorization Server 的登出功能之間共享。
以下範例示範如何註冊 SessionRegistry
@Bean
和 HttpSessionEventPublisher
@Bean
(SessionRegistryImpl
需要)
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}