栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

【OAuth2】十六、Spring Authorization Server如何生成并发放token的

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【OAuth2】十六、Spring Authorization Server如何生成并发放token的

这里写目录标题
  • 前言
  • 一、OAuth2TokenEndpointConfigurer
      • 1、关于authenticationProvider和authenticationProviders自定义的注意
  • 二、Token的生成
    • 1、OAuth2TokenGenerator的初始化
      • 1.1、JwtGenerator的初始化
      • 1.2、定义JWT的header和claims
      • 1.3、claimsOAuth2AccessTokenGenerator的初始化
      • 1.4、OAuth2RefreshTokenGenerator的初始化
      • 1.4、DelegatingOAuth2TokenGenerator
    • 2、小结
  • 三、OAuth2TokenEndpointFilter-token端点过滤器
    • 1、OAuth2TokenEndpointFilter的类结构
    • 2、DelegatingAuthenticationConverter参数提取
      • 2.1、授权码模式
      • 2.2、刷新token模式
      • 2.3、客户端凭据模式
  • 四、Token的认证
    • 1、token认证的三个方式
    • 2、认证成功处理
      • 2.1 DefaultOAuth2AccessTokenResponseMapConverter
    • 3、小结
  • 五、AuthenticationProvider
    • 1、OAuth2AuthorizationCodeAuthenticationProvider
    • 2、OAuth2RefreshTokenAuthenticationProvider
    • 3、 OAuth2ClientCredentialsAuthenticationProvider
    • 4、总结流程
    • 附OAuth2Authorization样例

前言

首先了解的人肯定知道 这个**/oauth2/token**路径是用来申请token的,那那么他是怎么生效的和怎么配置的呢

一、OAuth2TokenEndpointConfigurer

OAuth2TokenEndpointConfigurer用来配置Spring Authorization Server的OAuth2TokenEndpointFilter过滤器,这个过滤器用来处理客户端发来的/oauth2/token(默认)请求。配置项有以下五个:

其中requestMatcher需要通过ProviderSettings自定义,这里并没有自定义入口。

1、关于authenticationProvider和authenticationProviders自定义的注意

自定义accessTokenRequestConverter的话,就意味着你修改了默认的配置。而默认的配置是一个委托类,包含了三种策略(参见上图)。如果你的自定义配置没有适配这三种授权方式,将会失去对这三种方式的支持。
一旦你通过OAuth2TokenEndpointConfigurer#authenticationProvider(AuthenticationProvider)方法进行了自定义,默认提供的三种AuthenticationProvider也将自动失效。

二、Token的生成

OAuth2TokenGenerator是所有类型Token生成器的抽象。 目前有以下几个实现


OAuth2TokenGenerator的配置也是在这里完成的。那么它是从哪里初始化的呢?

1、OAuth2TokenGenerator的初始化

OAuth2TokenGenerator的初始化是借助于工具类OAuth2ConfigurerUtils的静态方法getTokenGenerator来生成。源码如下:

这里采用了单例懒加载设计:

1、先从SharedObject中获取,如果有就直接返回了。
2、如果SharedObject中没有就从Spring IoC中找找,再没有就开始初始化一个,初始化的逻辑专门分析,这里你把它当作黑盒,可以不打断你的思路。
3、初始化成功后,再放入SharedObject,下次再拿直接就有了。

1.1、JwtGenerator的初始化

上面初始化的步骤

你会发现它初始化的范式和上面OAuth2TokenGenerator差不多。优先从SharedObject中获取,没有就看看Spring IoC中有没有JwtEncoder或者JWKSource,有就能初始化一个JwtGenerator,无论有没有都会返回。

1.2、定义JWT的header和claims

这里有一个很好玩的东西OAuth2TokenCustomizer,这个是干啥的呢,这个是如果你JWT有自定义需求,主要定义JWT的header和claims,就可以定义一个该类型的Spring Bean就可以了。

 @Bean
    public OAuth2TokenCustomizer jwtTokenCustomizer(){
         return context -> {
             Authentication principal = context.getPrincipal();
             OAuth2Authorization authorization = context.getAuthorization();
             Set authorizedScopes = context.getAuthorizedScopes();
             Authentication authorizationGrant = context.getAuthorizationGrant();
             ProviderContext providerContext = context.getProviderContext();
             RegisteredClient registeredClient = context.getRegisteredClient();
             // 上面的都可以拿到
             // 目的是为了定制jwt 的header 和 claims
             JoseHeader.Builder headers = context.getHeaders();
             context.getClaims().audience(Arrays.asList("client1","client2"))
                   .claim("some","any");
         };
    }
1.3、claimsOAuth2AccessTokenGenerator的初始化

这个OAuth2AccessTokenGenerator其实就是个兜底的,它只能生成不透明令牌,如果JwtGenerator不存在,它就“扶正”了。你如果不喜欢使用JWT,就不要让JwtGenerator初始化。

类似于JwtGenerator的自定义接口OAuth2TokenCustomizer,OAuth2AccessTokenGenerator也有一个自定义接口OAuth2TokenCustomizer,这两个的用法非常类似,就不再赘述了。

1.4、OAuth2RefreshTokenGenerator的初始化

最后也会初始化一个刷新Token的生成器OAuth2RefreshTokenGenerator,它是一个不透明令牌。

1.4、DelegatingOAuth2TokenGenerator


最终我们使用的是一个代理委托类,按照策略进行生成。

  • 如果存在JwtGenerator,那就连同OAuth2AccessTokenGenerator和JwtGenerator组装一个代理生成器
  • 如果没有就只有OAuth2AccessTokenGenerator和JwtGenerator。
2、小结

对于其它配置都是老面孔了,AuthenticationSuccessHandler和AuthenticationFailureHandler已经多次提及,在OAuth2TokenEndpointFilter中都提供了默认实现,如果不满足需要可自行扩展,这里就不再赘述。下面我们会对OAuth2TokenEndpointFilter进行一个简单的分析和实践

三、OAuth2TokenEndpointFilter-token端点过滤器 1、OAuth2TokenEndpointFilter的类结构

该过滤器时在OAuth2TokenEndpointConfigurer中添加

OAuth2TokenEndpointFilter的属性字段,可以很明显的看出来默认的拦截端点

  • OAuth2TokenEndpointFilter的类图


    它会根据不同的授权方式AuthorizationGrantType(目前只支持authorization_code(授权码)、refresh_token(刷新)、client_credentials(客户)端三种授权方式)用不同策略提取请求中的授权信息,分别对应三种Token :

  • OAuth2AuthorizationCodeAuthenticationToken 授权码模式。

  • OAuth2RefreshTokenAuthenticationToken 刷新Token。

  • OAuth2ClientCredentialsAuthenticationToken 客户端凭据模式。

2、DelegatingAuthenticationConverter参数提取

DelegatingAuthenticationConverter负责维护token请求参数提取的策略,它有三种策略。接下来我们来看看如何从token请求中提取参数,并封装为Authentication。

2.1、授权码模式

授权码模式的参数提取由OAuth2AuthorizationCodeAuthenticationConverter负责。它的提取策略是:

  • 授权类型必须是授权码模式,也就是必须携带参数grant_type=authorization_code。
  • 必须携带有效的code值。
  • 必须携带一个redirect_uri。
    封装为OAuth2AuthorizationCodeAuthenticationToken:

    授权码根据客户端认证方式的不同请求也略有不同
  • client_secret_post方式
    除了携带grant_type、code、redirect_uri三个参数外,还携带了客户端的client_id和client_secret。
POST /oauth2/token HTTP/1.1
Host: localhost:9000
Content-Type: application/x-www-form-urlencoded
Content-Length: 218

grant_type=authorization_code&code=BAVzaAx8TtTTRE-E_CoQJ8-Bu9-APXjZOqVwPm7JxTQox3ko6d3aTM-m_p4aWoeEII6UDg5X9StWvTW5m9_0IvdCD2pJlaHVkLGnwXWzjXRPtU9hJVMvfI8VR-t8UvCL&redirect_uri=http%3A%2F%2F127.0.0.1%3A8082%2Ffoo%2Fbar&client_id=felord&client_secret=secret
  • client_secret_basic方式
    携带了grant_type、code、redirect_uri三个参数,并且附带了Basic Authorization请求头,规则BASE64.encode(client_id:client_secret)。
POST /oauth2/token HTTP/1.1
Host: localhost:9000
Authorization: Basic ZmVsb3JkOnNlY3JldA==
Content-Type: application/x-www-form-urlencoded
Content-Length: 218

grant_type=authorization_code&code=BAVzaAx8TtTTRE-E_CoQJ8-Bu9-APXjZOqVwPm7JxTQox3ko6d3aTM-m_p4aWoeEII6UDg5X9StWvTW5m9_0IvdCD2pJlaHVkLGnwXWzjXRPtU9hJVMvfI8VR-t8UvCL&redirect_uri=http%3A%2F%2F127.0.0.1%3A8082%2Ffoo%2Fbar
  • private_key_jwt或者client_secret_jwt方式
    除了携带grant_type、code、redirect_uri三个参数外,还必须携带client_id、client_assertion_type、assertion-type。这个参见前面对两种OAuth2客户端认证方式的讲解。
POST /oauth2/token HTTP/1.1
Host: localhost:9000
Content-Type: application/x-www-form-urlencoded
Content-Length: 1008

grant_type=authorization_code&code=5e7apu0SF720WKmQwVj-wx3lsEDkug1suSnqiXhvB6RwRkOjNrWN43n6DLmKXpcz3RaHG5gSFnvIth97nw-ltKRaDOtSkvl9LfN9YrivhfKG4Ln0Wqe1gmoXvhpyvD45&redirect_uri=http%3A%2F%2F127.0.0.1%3A8082%2Ffoo%2Fbar&client_id=felord&client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3Aclient-assertion-type%3Ajwt-bearer&client_assertion=eyJ4NXQjUzI1NiI6IlZ4YTJKMllTcnRFSkxPZlI2LU9zMXRPaXJfWXIzS0s2OVI5anJ1cTlzdmciLCJraWQiOiJqb3NlIiwiYWxnIjoiUlMyNTYifQ.eyJzdWIiOiJmZWxvcmQiLCJhdWQiOiJodHRwOlwvXC9sb2NhbGhvc3Q6OTAwMFwvb2F1dGgyXC90b2tlbiIsImlzcyI6ImZlbG9yZCIsImV4cCI6MTY1MDI2NDg4NCwiaWF0IjoxNjUwMjY0ODI0LCJqdGkiOiI4MDk5ZWI1Mi05ZTI1LTQ2OTgtYjQwMS1iMjc5MmNhNmI0YmIifQ.sjZBUP5-uQzNQo634B-WCL2yYZt5fktfqeCXLS8qqdCTsGQrm07RGVk774h-VImg3CF5-0v2_aA5CfI1ESMNTmNwfyPLzDWpzInDI6_MX-tLta67TXGButvov0SrXjI8NPcw3IIlfQ91TBs5Msx_W-zpL4A_Px0cr8JuCFiAf092E_Yi7nTqJqwuETopIcSnPDeJsw9ReYsaHEbJ-2570IPcJP357t7RDT7JCJJYIruweIMO6fMAGTksz2cOQNmXd-bDcNE5Oaqm8vZ_2vRF4LuJ19WKM_RHQKwIId9yRrTsRd4rjlCHPYj95NZSyfqWeVtUezDMCjnPit3PaY31oQ
2.2、刷新token模式

access_token过期后,OAuth2客户端可以携带refresh_token通过**/oauth2/token**去请求一个新的令牌。参数提取由OAuth2RefreshTokenAuthenticationConverter负责。它的提取策略为:

  • 必须携带参数grant_type=refresh_token。
  • 必须携带有效的refresh_token值。
  • 可以携带授权范围值scope,当然这个是可选的。
    封装为OAuth2RefreshTokenAuthenticationToken:

    刷新token的请求根据不同的OAuth2认证方式也是不一样的,都需要组装携带它们各自特色的客户端认证参数,把上面授权码模式中的grant_type值替换为refresh_token、把code=替换refresh_token=即可,这里就不再一一演示了。
2.3、客户端凭据模式

客户端凭据模式由OAuth2ClientCredentialsAuthenticationConverter负责提取参数。它的提取策略为:

  • 必须携带参数grant_type=client_credentials。
  • 可以携带授权范围值scope,当然这个是可选的。
    封装为OAuth2ClientCredentialsAuthenticationToken:
四、Token的认证 1、token认证的三个方式

根据Spring Security的范式,封装为AuthenticationToken自然要交给认证管理器AuthenticationManager,由它检索出对应的AuthenticationProvider来认证AuthenticationToken。

根据上一篇的讲解,我们可以知道默认提供了三个AuthenticationProvider:

  • OAuth2AuthorizationCodeAuthenticationProvider
  • OAuth2RefreshTokenAuthenticationProvider
  • OAuth2ClientCredentialsAuthenticationProvider
    依次对授权码模式、刷新模式、客户端凭据模式的token请求进行认证处理,基于篇幅的原因,它们的逻辑我将分章节进行讲解。
2、认证成功处理

认证成功后会交给authenticationSuccessHandler进行处理,它的逻辑为:

2.1 DefaultOAuth2AccessTokenResponseMapConverter

写入响应的逻辑由DefaultOAuth2AccessTokenResponseMapConverter负责,我觉得有必要学习一下,它是HttpMessageConverter的一个实现。它首先它借助于内部的DefaultMapOAuth2AccessTokenResponseConverter将OAuth2AccessTokenResponse转换为Map;然后借助于JSON转换器进行序列化并写入响应。

如果想改变返回体的结构,可以通过其setAccessTokenResponseParametersConverter方法改写DefaultMapOAuth2AccessTokenResponseConverter。
3、小结

/oauth2/token返回的token只能交给OAuth2客户端使用,不能交给其它User Agent使用,这是非常不安全的。只有注册在授权服务器具有client_id的才是OAuth2客户端。很多同学都混淆了这一点,我们只能通过令牌中继间接的通过已授权的OAuth2客户端来使用token请求资源。

五、AuthenticationProvider

上面分析了OAuth2TokenEndpointFilter,它处理了授权码模式、刷新模式、客户端凭据模式三种Token请求的逻辑,正好对应了三个AuthenticationProvider。

1、OAuth2AuthorizationCodeAuthenticationProvider

这个名字的类有两个,请认准
org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthorizationCodeAuthenticationProvider

  1. 获取当前验证码对应的OAuth2客户端信息。

  2. 然后根据携带的授权码code检索出在之前授权码请求中存储的授权信息OAuth2Authorization进行条件判定,以证明本次请求合规。

  3. 首先,查询的客户端信息和请求的客户端要一致,如果发现冒用也要强行过期掉OAuth2Authorization。

  4. 其次,redirectUri也要一致。

  5. 授权码必须在有效期内,否则也不行。

  6. 组装构造AccessToken的上下文,包含了:

  • OAuth2客户端信息RegisteredClient。
  • 资源拥有者的认证信息Principal。
  • 授权服务器的上下文信息ProviderContext。
  • 本次授权的信息OAuth2Authorization。
  • 已授权范围。
  • 授权类型,自然是授权码方式,来自AuthorizationGrantType。
  • 本次Token请求的OAuth2AuthorizationCodeAuthenticationToken。
  • Token类型OAuth2TokenType,这里ACCESS_TOKEN。
  1. 通过Token生成器将步骤⑥的上下文对象转换为OAuth2Token,这里实际是Jwt。

  2. 依据步骤⑦生成的Jwt初始化OAuth2AccessToken。

  3. 会把OAuth2AccessToken一起写入OAuth2Authorization,如果令牌是是Jwt风格,会把令牌包含的claims也写进去,后面的刷新Token、ID Token都是这样。

  4. 刷新Token的生成,步骤非常简单不再赘述,值得一提的是它需要同时满足以下条件:

  • OAuth2客户端支持刷新Token。
  • OAuth2客户端不是公共客户端(ClientAuthenticationMethod.NONE)。
  1. OIDC专属的Id Token的生成,条件是请求必须属于OIDC认证。

  2. 步骤⑧⑩⑾生成的Token信息都会被存入 OAuth2Authorization,同时本次授权的授权码会被主动作废,OAuth2AuthorizationService会对OAuth2Authorization的持久化进行更新。

最后注册客户端信息RegisteredClient、客户端认证信息OAuth2ClientAuthenticationToken、访问令牌OAuth2AccessToken、刷新令牌(可能为null)OAuth2RefreshToken,以及可能包含OidcIdToken的additionalParameters组成了OAuth2AccessTokenAuthenticationToken返回

2、OAuth2RefreshTokenAuthenticationProvider

Spring Authorization Server刷新Token的逻辑由该AuthenticationProvider负责。刷新令牌请求中包含了以下三个重要的东西:

  • 刷新令牌,令牌必须有效。
  • 授权范围grant_type,必须是refresh_token。
  • 授权范围scope,这个是可选的,有一个重要的点你必须

记住:刷新令牌携带的scope必须是没有受过权的,否则将抛出异常;另外如果不携带该参数则刷新后的访问令牌默认范围是上次授权的范围。
这个AuthenticationProvider逻辑上非常简单,依旧是对参数进行了校验,生成token的步骤和OAuth2AuthorizationCodeAuthenticationProvider类似。一个重要的差别就是,如果你的客户端设置TokenSettings开启了重用刷新令牌isReuseRefreshTokens=ture,那么刷新令牌就可以重复使用。

3、 OAuth2ClientCredentialsAuthenticationProvider

客户端凭据模式的Token签发由该AuthenticationProvider负责。

@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication =
				(OAuth2ClientCredentialsAuthenticationToken) authentication;
       //①获取当前验证码对应的OAuth2客户端信息。
		OAuth2ClientAuthenticationToken clientPrincipal =
				getAuthenticatedClientElseThrowInvalidClient(clientCredentialsAuthentication);
		RegisteredClient registeredClient = clientPrincipal.getRegisteredClient();
// ②判断是不是客户端凭据模式。
		if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.CLIENT_CREDENTIALS)) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
		}

//③如果请求中携带的授权范围scope不为空,就校验一下有没有超出客户端定义的范围;如果没有超出就按照请求中的去设定;如果请求中不携带scope的话就把客户端定义的全部范围赋予给本次请求。
		Set authorizedScopes = registeredClient.getScopes();		// Default to configured scopes
		if (!CollectionUtils.isEmpty(clientCredentialsAuthentication.getScopes())) {
			for (String requestedScope : clientCredentialsAuthentication.getScopes()) {
				if (!registeredClient.getScopes().contains(requestedScope)) {
					throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
				}
			}
			authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());
		}

 // ④组装构造AccessToken的上下文,细节参见OAuth2AuthorizationCodeAuthenticationProvider。
		// @formatter:off
		OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()
				.registeredClient(registeredClient)
				.principal(clientPrincipal)
				.providerContext(ProviderContextHolder.getProviderContext())
				.authorizedScopes(authorizedScopes)
				.tokenType(OAuth2TokenType.ACCESS_TOKEN)
				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
				.authorizationGrant(clientCredentialsAuthentication)
				.build();
		// @formatter:on

// ⑤通过Token生成器将步骤④的上下文对象转换为OAuth2Token,这里通常是Jwt。
	OAuth2Token generatedAccessToken = this.tokenGenerator.generate(tokenContext);
		if (generatedAccessToken == null) {
			OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR,
					"The token generator failed to generate the access token.", ERROR_URI);
			throw new OAuth2AuthenticationException(error);
		}

// ⑥依据步骤⑤生成的OAuth2Token初始化OAuth2AccessToken。
		OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER,
				generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(),
				generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes());

		// @formatter:off
		OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.withRegisteredClient(registeredClient)
				.principalName(clientPrincipal.getName())
				.authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)
				.attribute(OAuth2Authorization.AUTHORIZED_SCOPE_ATTRIBUTE_NAME, authorizedScopes);
		
		// @formatter:on
		if (generatedAccessToken instanceof ClaimAccessor) {
		authorizationBuilder.token(accessToken, (metadata) ->			metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()));
		} else {
	authorizationBuilder.accessToken(accessToken);
		}
		OAuth2Authorization authorization = authorizationBuilder.build();
	//  ⑦把授权信息持久化。
	this.authorizationService.save(authorization);
    //⑧生成已授权的信息OAuth2AccessTokenAuthenticationToken。
		return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken);
	}

  • ①获取当前验证码对应的OAuth2客户端信息。
  • ②判断是不是客户端凭据模式。
  • ③如果请求中携带的授权范围scope不为空,就校验一下有没有超出客户端定义的范围;如果没有超出就按照请求中的去设定;如果请求中不携带scope的话就把客户端定义的全部范围赋予给本次请求。
  • ④组装构造AccessToken的上下文,细节参见OAuth2AuthorizationCodeAuthenticationProvider。
  • ⑤通过Token生成器将步骤④的上下文对象转换为OAuth2Token,这里通常是Jwt。
  • ⑥依据步骤⑤生成的OAuth2Token初始化OAuth2AccessToken。
  • ⑦把授权信息持久化。
  • ⑧生成已授权的信息OAuth2AccessTokenAuthenticationToken。
4、总结流程

大体上,都是通过token端点传递参数,然后根据各自的AuthenticationConverter从请求中提取参数并封装成Authentication,Authentication会按照授权类型(grant_type)的策略从三种AuthenticationProvider中选择一个定向处理,根据不同的逻辑生成最终授权的OAuth2AccessTokenAuthenticationToken。

附OAuth2Authorization样例
{
    "id": "a1b0c6cb-45d2-46ab-a418-552e025ce719",
    "access_token_expires_at": null,
    "access_token_issued_at": null,
    "access_token_metadata": null,
    "access_token_scopes": null,
    "access_token_type": null,
    "access_token_value": null,
    "attributes": "{"@class":"java.util.Collections$UnmodifiableMap","java.security.Principal":{"@class":"org.springframework.security.authentication.UsernamePasswordAuthenticationToken","authorities":["java.util.Collections$UnmodifiableRandomAccessList",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"details":{"@class":"org.springframework.security.web.authentication.WebAuthenticationDetails","remoteAddress":"0:0:0:0:0:0:0:1","sessionId":"9BA3FCEFA50ED24B42259BA062C6C4B2"},"authenticated":true,"principal":{"@class":"org.springframework.security.core.userdetails.User","password":null,"username":"felord","authorities":["java.util.Collections$UnmodifiableSet",[{"@class":"org.springframework.security.core.authority.SimpleGrantedAuthority","authority":"ROLE_USER"}]],"accountNonExpired":true,"accountNonLocked":true,"credentialsNonExpired":true,"enabled":true},"credentials":null},"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest":{"@class":"org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationRequest","authorizationUri":"http://localhost:9000/oauth2/authorize","authorizationGrantType":{"value":"authorization_code"},"responseType":{"value":"code"},"clientId":"e2fa7e64-249b-46f0-ae1d-797610e88615","redirectUri":"http://127.0.0.1:8082/foo/bar","scopes":["java.util.Collections$UnmodifiableSet",["message.read","message.write"]],"state":"noihJd3-Mc_nEbAx8As0aUEwJraRUHQAbcfQ87F2FtE=","additionalParameters":{"@class":"java.util.Collections$UnmodifiableMap"},"authorizationRequestUri":"http://localhost:9000/oauth2/authorize?response_type=code&client_id=e2fa7e64-249b-46f0-ae1d-797610e88615&scope=message.read%20message.write&state=noihJd3-Mc_nEbAx8As0aUEwJraRUHQAbcfQ87F2FtE%3D&redirect_uri=http://127.0.0.1:8082/foo/bar","attributes":{"@class":"java.util.Collections$UnmodifiableMap"}},"state":"-PS0r6frBqz8QTBlFpQXKdJnBlZG5FMmHQ5Nziwuy0Y="}",
    "authorization_code_expires_at": null,
    "authorization_code_issued_at": null,
    "authorization_code_metadata": null,
    "authorization_code_value": null,
    "authorization_grant_type": "authorization_code",
    "oidc_id_token_claims": null,
    "oidc_id_token_expires_at": null,
    "oidc_id_token_issued_at": null,
    "oidc_id_token_metadata": null,
    "oidc_id_token_value": null,
    "principal_name": "test",
    "refresh_token_expires_at": null,
    "refresh_token_issued_at": null,
    "refresh_token_metadata": null,
    "refresh_token_value": null,
    "registered_client_id": "2c9c20818099c695018099cbca030000",
    "state": "-PS0r6frBqz8QTBlFpQXKdJnBlZG5FMmHQ5Nziwuy0Y="
  }
转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/1041254.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号