本站(springdoc.cn)中的内容来源于 spring.io ,原始版权归属于 spring.io。由 springboot.io - Spring Boot中文社区 进行翻译,整理。可供个人学习、研究,未经许可,不得进行任何转载、商用或与之相关的行为。 商标声明:Spring 是 Pivotal Software, Inc. 在美国以及其他国家的商标。

RegisteredClient

RegisteredClient 是一个已在授权服务器上 注册的 客户端的表示。客户端必须在授权服务器上注册,然后才能启动授权流程,如 authorization_codeclient_credentials

在客户端注册期间,客户端被分配了一个唯一的 客户端标识符(client id),(可选)一个 client secret(取决于 客户端类型),以及与其唯一 client id 相关的元数据。客户端的元数据范围可以从面向人类的显示字符串(如客户名称)到协议流的特定项目(如有效重定向URI列表)。

在Spring Security的OAuth2客户端支持中,相应的客户端注册 model 是 ClientRegistration

客户端的主要目的是请求访问受保护的资源。客户端首先通过与授权服务器进行认证并出示授权许可来请求访问令牌(access token)。授权服务器对客户和授权许可进行认证,如果它们是有效的,就会发出一个访问令牌。客户端现在可以通过出示访问令牌从资源服务器请求受保护的资源。

下面的例子显示了如何配置一个允许执行 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的 NoOpPasswordEncoderPasswordEncoder 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 的元数据(属性)与其独特的 Client Id 相关,定义如下。

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> scopes; (10)
	private ClientSettings clientSettings;  (11)
	private TokenSettings tokenSettings;    (12)

	...

}
1 id: RegisteredClient 的唯一 ID。
2 clientId: client id。
3 clientIdIssuedAt: client id 签发时间。
4 clientSecret: client secret,该值应使用 Spring Security 的 PasswordEncoder 进行编码。
5 clientSecretExpiresAt: client secret 过期时间。
6 clientName: 一个用于描述客户端名称。该名称可能会在某些情况下使用,例如在同意页中显示客户端名称时。
7 clientAuthenticationMethods: 客户端可能使用的认证方法。支持的值是 client_secret_basicclient_secret_postprivate_key_jwtclient_secret_jwtnone公共客户端)。
8 authorizationGrantTypes: 客户端可以使用的 授权许可类型。支持的值是 authorization_codeclient_credentialsrefresh_token
9 redirectUris: 注册的 重定向URI,客户端可以在基于重定向的流程中使用—​例如,authorization_code 授权。
10 scopes: 客户允许请求的scope。
11 clientSettings: 客户端的自定义设置—​例如,需要 PKCE,需要授权许可,以及其他。
12 tokenSettings: 发给客户端的OAuth2令牌的自定义设置 - 例如,访问/刷新令牌的生存时间,重用刷新令牌,以及其他。

RegisteredClientRepository

RegisteredClientRepository 是一个中心组件,新的客户端可以在这里注册,现有的客户端可以在这里查询。当遵循特定的协议流程时,它被其他组件使用,如客户端认证、授权许可处理、令牌内省、动态客户端注册等。

RegisteredClientRepository 提供的实现是 InMemoryRegisteredClientRepositoryJdbcRegisteredClientRepositoryInMemoryRegisteredClientRepository 实现在内存中存储 RegisteredClient 实例,建议只在开发和测试期间使用。 JdbcRegisteredClientRepository 是一个JDBC实现,通过使用 JdbcOperations 来持久化 RegisteredClient 实例。

RegisteredClientRepository 是一个 REQUIRED 组件。

下面的例子显示了如何注册 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客户端支持中,相应的授权 model 是 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: 资源所有者(或客户端)的 principal 名称。
4 authorizationGrantType: 使用的 AuthorizationGrantType
5 authorizedScopes: 为客户授权的 scope 集。
6 tokens: 特定于已执行的授权许可类型的 OAuth2Token 实例(和相关元数据)。
7 attributes: 所执行的授权许可类型所特有的额外属性—​例如,经过验证的 PrincipalOAuth2AuthorizationRequest 以及其他。

OAuth2Authorization 和其相关的 OAuth2Token 实例有一个设定的生命周期。一个新发布的 OAuth2Token 是活跃的,当它过期或被无效(撤销)时,就会变得不活跃。当所有相关的 OAuth2Token 实例不活跃时,OAuth2Authorization 就(隐含地)不活跃了。每个 OAuth2Token 被保存在一个 OAuth2Authorization.Token 中,它提供了 isExpired()isInvalidated()isActive() 的访问器(accessor)。

OAuth2Authorization.Token 还提供了 getClaims(),它返回与 OAuth2Token 相关的 claim(如果有的话)。

OAuth2AuthorizationService

OAuth2AuthorizationService 是一个中心组件,新的授权被存储,现有的授权被查询。当遵循特定的协议流程时,它被其他组件使用—​例如,客户端认证、授权许可处理、令牌内省、令牌撤销、动态客户端注册等。

OAuth2AuthorizationService 的实现是 InMemoryOAuth2AuthorizationServiceJdbcOAuth2AuthorizationServiceInMemoryOAuth2AuthorizationService 实现在内存中存储 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 是一个来自 OAuth2授权请求流 的授权 "consent" 的表示,例如,authorization_code grant,它持有资源所有者授予客户端的权限。

当授权给客户时,资源所有者可以只授予客户要求的权限的一个子集。典型的用例是 authorization_code 授权流程,在该流程中,客户端请求 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: 资源所有者的 principal 名称。
3 authorities: 由资源所有者授予客户端的权限。一个授权可以代表一个scope、一个claim、一个许可(permission)、一个角色,以及其他。

OAuth2AuthorizationConsentService 是一个中心组件,新的授权同意被存储,现有的授权同意被查询。它主要被实现OAuth2授权请求流程的组件使用—​例如,authorization_code grant。

OAuth2AuthorizationConsentService 的实现是 InMemoryOAuth2AuthorizationConsentServiceJdbcOAuth2AuthorizationConsentServiceInMemoryOAuth2AuthorizationConsentService 实现在内存中存储 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 相关信息的 context 对象,并由 OAuth2TokenGeneratorOAuth2TokenCustomizer 使用。

OAuth2TokenContext 提供以下访问器(accessor)。

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(): 授权给客户的scope。
6 getTokenType(): 要生成的 OAuth2TokenType。支持的值是代码、access_tokenrefresh_tokenid_token
7 getAuthorizationGrantType(): 与授权许可相关的 AuthorizationGrantType
8 getAuthorizationGrant(): 处理授权的 AuthenticationProvider 所使用的 Authentication 实例。

OAuth2TokenGenerator

OAuth2TokenGenerator 负责从所提供的 OAuth2TokenContext 中的信息生成 OAuth2Token

生成的 OAuth2Token 主要取决于在 OAuth2TokenContext 中指定的 OAuth2TokenType

例如,当 OAuth2TokenTypevalue 为:

  • code, 则生成 OAuth2AuthorizationCode

  • access_token, 则生成 OAuth2AccessToken

  • refresh_token, 则生成 OAuth2RefreshToken

  • id_token, 则生成 OidcIdToken

此外,生成的 OAuth2AccessToken 的格式是不同的,取决于为 RegisteredClient 配置的 TokenSettings.getAccessTokenFormat()。如果格式是 OAuth2TokenFormat.SELF_CONTAINED(默认),那么就会生成一个 Jwt。如果格式是 OAuth2TokenFormat.REFERENCE,那么就会生成一个 "opaque" token。

最后,如果生成的 OAuth2Token 有一组 claim,并且实现了 ClaimAccessor,那么这些 claim 就可以从 OAuth2Authorization.Token.getClaims() 中获得。

OAuth2TokenGenerator 主要由实现授权许可处理的组件使用—​例如,authorization_codeclient_credentialsrefresh_token

提供的实现是 OAuth2AccessTokenGeneratorOAuth2RefreshTokenGeneratorJwtGeneratorOAuth2AccessTokenGenerator 生成一个 "opaque"(OAuth2TokenFormat.REFERENCE)access token,而 JwtGenerator 生成一个 JwtOAuth2TokenFormat.SELF_CONTAINED)。

OAuth2TokenGenerator 是一个可选的组件,默认为由 OAuth2AccessTokenGeneratorOAuth2RefreshTokenGenerator 组成的 DelegatingOAuth2TokenGenerator
如果注册了 JwtEncoder @BeanJWKSource<SecurityContext> @Bean,那么在 DelegatingOAuth2TokenGenerator 中还会额外组成一个 JwtGenerator

OAuth2TokenGenerator 提供了极大的灵活性,因为它可以为 access_tokenrefresh_token 支持任何自定义的 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(实现 OAuth2TokenContext)为泛型声明的 OAuth2TokenCustomizer<OAuth2TokenClaimsContext> 提供了定制 "opaque" OAuth2AccessToken 的能力。OAuth2TokenClaimsContext.getClaims() 提供了对 OAuth2TokenClaimsSet.Builder 的访问,允许添加、替换和删除 claim。

下面的例子显示了如何实现一个 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(实现 OAuth2TokenContext)为泛型类型的 OAuth2TokenCustomizer<JwtEncodingContext> 提供了定制 Jwt 的header和claim的能力。JwtEncodingContext.getHeaders() 提供了对 JwsHeader.Builder 的访问,允许添加、替换和删除header。JwtEncodingContext.getClaims() 提供了对 JwtClaimsSet.Builder 的访问,允许添加、替换和删除claim。

下面的例子展示了如何实现 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.getHeaders();
		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 Token 的例子,请参见指南 How-to:定制 OpenID Connect 1.0 UserInfo 响应