核心模型 / 组件
RegisteredClient(已注册客户端)
RegisteredClient
是在授权服务器注册的客户端的表示。客户端必须在授权服务器注册后才能启动授权授予流程,例如authorization_code
或client_credentials
。
在客户端注册期间,客户端被分配一个唯一的客户端标识符,(可选地)一个客户端密钥(取决于客户端类型),以及与其唯一客户端标识符相关联的元数据。客户端的元数据可以从面向人类的显示字符串(例如客户端名称)到特定于协议流的项目(例如有效重定向URI列表)。
Spring Security OAuth2客户端支持中对应的客户端注册模型是ClientRegistration。 |
客户端的主要目的是请求访问受保护的资源。客户端首先通过与授权服务器进行身份验证并呈现授权授予来请求访问令牌。授权服务器对客户端和授权授予进行身份验证,如果它们有效,则发放访问令牌。客户端现在可以通过呈现访问令牌向资源服务器请求受保护资源。
以下示例显示了如何配置一个允许执行authorization_code授予流以请求访问令牌的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客户端支持中的相应配置为:
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: http://localhost: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 :客户端可以使用的授权授予类型。支持的值包括authorization_code 、client_credentials 、refresh_token 和urn:ietf:params:oauth:grant-type:device_code 。 |
9 | redirectUris :客户端可以在基于重定向的流程中使用的注册的重定向URI。例如,authorization_code 授予。 |
10 | postLogoutRedirectUris :客户端可以用于注销的后重定向URI。 |
11 | scopes :客户端被允许请求的范围。 |
12 | clientSettings :客户端的自定义设置,例如需要PKCE、需要授权同意等。 |
13 | tokenSettings :发放给客户端的OAuth2令牌的自定义设置,例如访问/刷新令牌的生存时间、重用刷新令牌等。 |
RegisteredClientRepository
RegisteredClientRepository
是一个中心组件,用于注册新客户端和查询现有客户端。在遵循特定协议流程时,其他组件会使用它,例如客户端认证、授权授予处理、令牌检查、动态客户端注册等。
RegisteredClientRepository
的提供的实现有InMemoryRegisteredClientRepository
和JdbcRegisteredClientRepository
。 InMemoryRegisteredClientRepository
实现将RegisteredClient
实例存储在内存中,建议仅在开发和测试期间使用。 JdbcRegisteredClientRepository
是一个使用JdbcOperations
持久化RegisteredClient
实例的JDBC实现。
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
授权授予类型的情况下由客户端本身授予给客户端。
Spring Security的OAuth2客户端支持中对应的授权模型是OAuth2AuthorizedClient。 |
在成功完成授权授予流程后,将创建一个OAuth2Authorization
,并关联一个OAuth2AccessToken
,一个(可选的)OAuth2RefreshToken
,以及与执行的授权授予类型相关的其他状态。
与OAuth2Authorization
关联的OAuth2Token
实例因授权授予类型的不同而有所不同。
对于OAuth2的authorization_code授予,将关联一个OAuth2AuthorizationCode
,一个OAuth2AccessToken
,以及一个(可选的)OAuth2RefreshToken
。
对于OpenID Connect 1.0的authorization_code授予,将关联一个OAuth2AuthorizationCode
,一个OidcIdToken
,一个OAuth2AccessToken
,以及一个(可选的)OAuth2RefreshToken
。
对于OAuth2的client_credentials授予,只会关联一个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 :为客户端授权的作用域的Set 。 |
6 | tokens :与执行的授权授予类型相关的OAuth2Token 实例(及相关元数据)。 |
7 | attributes :与执行的授权授予类型相关的其他属性 - 例如,经过身份验证的Principal 、OAuth2AuthorizationRequest 等。 |
OAuth2Authorization
及其关联的OAuth2Token
实例具有一定的生命周期。新发行的OAuth2Token
在到期或失效(被撤销)时变为无效。当所有关联的OAuth2Token
实例都无效时,OAuth2Authorization
(隐式地)变为无效。每个OAuth2Token
都保存在一个OAuth2Authorization.Token
中,提供了isExpired()
、isInvalidated()
和isActive()
的访问器。
OAuth2Authorization.Token
还提供了getClaims()
,返回与OAuth2Token
相关联的声明(如果有)。
OAuth2AuthorizationService
OAuth2AuthorizationService
是一个中心组件,用于存储新的授权并查询现有的授权。在遵循特定协议流程时,其他组件会使用它 - 例如,客户端认证、授权授予处理、令牌内省、令牌撤销、动态客户端注册等。
OAuth2AuthorizationService
的提供的实现有InMemoryOAuth2AuthorizationService
和JdbcOAuth2AuthorizationService
。 InMemoryOAuth2AuthorizationService
实现将OAuth2Authorization
实例存储在内存中,建议仅在开发和测试期间使用。 JdbcOAuth2AuthorizationService
是一个使用JdbcOperations
持久化OAuth2Authorization
实例的JDBC实现。
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
授予,其中包含资源所有者授予给客户端的权限。
在授权访问给客户端时,资源所有者可能仅授予客户端请求的权限子集。典型用例是authorization_code
授予流程,其中客户端请求范围,资源所有者授予(或拒绝)对请求范围的访问。
在完成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 :资源所有者授予客户端的权限。权限可以表示范围、声明、权限、角色等。 |
OAuth2AuthorizationConsentService
OAuth2AuthorizationConsentService
是一个中心组件,用于存储新的授权同意并查询现有的授权同意。主要由实现OAuth2授权请求流程的组件使用 - 例如,authorization_code
授予。
OAuth2AuthorizationConsentService
的提供的实现有InMemoryOAuth2AuthorizationConsentService
和JdbcOAuth2AuthorizationConsentService
。 InMemoryOAuth2AuthorizationConsentService
实现将OAuth2AuthorizationConsent
实例存储在内存中,仅建议用于开发和测试。 JdbcOAuth2AuthorizationConsentService
是一个使用JdbcOperations
持久化OAuth2AuthorizationConsent
实例的JDBC实现。
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() : 与授权授予相关联的RegisteredClient。 |
2 | getPrincipal() : 资源所有者(或客户端)的Authentication 实例。 |
3 | getAuthorizationServerContext() : 包含授权服务器运行时环境信息的AuthorizationServerContext 对象。 |
4 | getAuthorization() : 与授权授予相关联的OAuth2Authorization。 |
5 | getAuthorizedScopes() : 为客户端授权的范围。 |
6 | getTokenType() : 要生成的OAuth2TokenType 。支持的值包括code 、access_token 、refresh_token 和id_token 。 |
7 | getAuthorizationGrantType() : 与授权授予相关联的AuthorizationGrantType 。 |
8 | getAuthorizationGrant() : 由处理授权授予的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
,则生成一个“opaque”令牌。
最后,如果生成的OAuth2Token
具有一组声明并实现了ClaimAccessor
,则可以从OAuth2Authorization.Token.getClaims()访问这些声明。
OAuth2TokenGenerator
主要由实现授权授予处理的组件使用,例如authorization_code
、client_credentials
和refresh_token
。
提供的实现包括OAuth2AccessTokenGenerator
、OAuth2RefreshTokenGenerator
和JwtGenerator
。 OAuth2AccessTokenGenerator
生成一个“opaque”(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
之前自定义OAuth2Token
的属性。
声明为OAuth2TokenCustomizer<OAuth2TokenClaimsContext>
,泛型类型为OAuth2TokenClaimsContext
(implements OAuth2TokenContext
),提供了自定义"opaque" 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();
// 自定义声明
};
}
如果未提供OAuth2TokenGenerator 作为@Bean 或未通过OAuth2AuthorizationServerConfigurer 进行配置,则将自动配置OAuth2TokenCustomizer<OAuth2TokenClaimsContext> @Bean ,并配置为OAuth2AccessTokenGenerator 。 |
声明为OAuth2TokenCustomizer<JwtEncodingContext>
,泛型类型为JwtEncodingContext
(implements OAuth2TokenContext
),提供了自定义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)) {
// 为access_token自定义头部/声明
} else if (context.getTokenType().getValue().equals(OidcParameterNames.ID_TOKEN)) {
// 为id_token自定义头部/声明
}
};
}
如果未提供OAuth2TokenGenerator 作为@Bean 或未通过OAuth2AuthorizationServerConfigurer 进行配置,则将自动配置OAuth2TokenCustomizer<JwtEncodingContext> @Bean ,并配置为JwtGenerator 。 |
有关如何自定义ID token的示例,请参阅指南如何:自定义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授权服务器的注销功能之间共享。
以下示例展示了如何注册SessionRegistry
@Bean
和HttpSessionEventPublisher
@Bean
(SessionRegistryImpl
所需):
@Bean
public SessionRegistry sessionRegistry() {
return new SessionRegistryImpl();
}
@Bean
public HttpSessionEventPublisher httpSessionEventPublisher() {
return new HttpSessionEventPublisher();
}