没有文档的情况下,只能看代码,来知道系统是怎么设置的。
①②③④⑤⑥⑦⑧⑨⑩⑪⑫⑬⑭⑮⑯⑰⑱⑲⑳✕✓✔✖
官方文档上给出了一些核心的类,可以按照下面的思路来研究源代码。
OAuth2AuthorizationServerConfigurer
OAuth2AuthorizationServerConfiguration
ProviderSettings
/ ProviderContext
RegisteredClientRepository
/ RegisteredClient
OAuth2AuthorizationService
/ OAuth2Authorization
OAuth2AuthorizationConsentService
/ OAuth2AuthorizationConsent
JwtEncoder
OAuth2TokenCustomizer
/ OAuth2TokenContext
Endpoint | Class | Description |
---|---|---|
GET "/oauth2/authorize" | OAuth2AuthorizationEndpointFilter | OAuth 2.0 Authorization Endpoint |
POST "/oauth2/token" | OAuth2TokenEndpointFilter | obtain Access Token |
"/oauth2/introspect" | OAuth2TokenIntrospectionEndpointFilter | 内省地址 |
"/oauth2/revoke" | OAuth2TokenRevocationEndpointFilter | 撤销 Refresh Token |
GET "/.well-known/oauth-authorization-server" | OAuth2AuthorizationServerMetadataEndpointFilter | 获取服务器的 Metadata,可以获取服务器支持的参数列表 |
GET "/oauth2/jwks" | NimbusJwkSetEndpointFilter | 显示公钥 |
"/.well-known/openid-configuration" | OidcProviderConfigurationEndpointFilter | |
"/userinfo" | OidcUserInfoEndpointFilter | |
"/connect/register" | OidcClientRegistrationEndpointFilter |
GET "/oauth2/jwks" 的结果
{"keys": [{"kty": "RSA","e": "AQAB","kid": "82b83721-051a-460a-901b-4c209d2c3ab6","n": "oZZen-gDdCb5ZlF26ZOTqWfyLG0wKfsQ8UuUyyu9BlE1WlAWIuR8EM46Unonr-m4TZx7CpwPqe0CYwmHCADiFdS_ac7JKuNtLLaVH5friqKPueHq-ZFjnLdxkg2HJBXQSIj8qwopbQU7TSC9c1i4-GNmNjgfQ60jL8mU2b85zZQId3Ir7UT5qnYZCffbfa1ZFXqmnKcin1hLSwPTaXwKDAggBulTzgkzYsZxG0_QhXRbcrOt3FIrxxbqBKtOUxgaLVfeY_EPYJriHeezGbNKHDXr_WNJfNFp4hFXNYEe6tyulYnh9KBcC23dPhQst2FSsJ8gr-X3h46yNeGEaMTimw"}]}
JWS, JWE, JWK, JWA, JWT
JWS:JSON Web Signature,Digital signature/HMAC specification
JWE:JSON Web Encryption,Encryption specification
JWK:JSON Web Key,Public key specification:公钥规范
JWA:JSON Web Algorithms,Algorithms and identifiers specification
JWT:JSON Web Token
当出现问题时? 如何跟踪这个程序呢? 建议从 Endpoint 入口开始:
OAuth2*Token*Endpoint
OAuth2TokenEndpointFilter
OAuth2*Token*Endpoint
的OAuth2AuthorizationEndpointFilter
实际上根据参数的不同,分成普通 code 与 PKCE 两个分支。这里有两个流程的详细说明。
定义了要拦截的 URL,这里情况比较复杂,出了要拦截 Get 请求,还要区分是否是确认页。
doFilterInternal
这是一个主函数,里面包含了完整的逻辑链条
request
到Authentcation
sendAuthorizationResponse
,返回信息,包含CODE STATE RedirectUri
。这里描述一下上面的 2.1 中转换request到Authentcation
的逻辑
additionalParameters
,这样就可以自定义参数了生成OAuth2AuthorizationCodeRequestAuthenticationToken
对象,这个逻辑比较简单,就不详细描述了。
调用AuthenticationManager
的authenticate
。AuthenticationManager
包含了好多处理的类,其中会找到OAuth2AuthorizationCodeRequestAuthenticationProvider
OAuth2AuthorizationCodeRequestAuthenticationProvider
的authenticate
。这是一个重点的类,根据ClientID
得到registeredClient
。然后与用户传入的参数进行核对,核对的逻辑如下;
redirect_uri
AuthorizationGrantType
与设置的是否一致scope
codeChallenge
codeChallengeMethod
是否存在require-proof-key=true
,也就是必须有,那么就抛出错误return isAuthenticated() is false
,然后就跳转到登陆页面OAuth2AuthorizationRequest
authorizationConsentService
,找到当前的OAuth2AuthorizationConsent
return
,同时标记需要确认页面authorizationCodeGenerator生成code
OAuth2Authorization
,并调用authorizationService
保存起来,这是需要实例化redirectUri
为空,就找设置中默认的那个return
一个包含Code
的OAuth2AuthorizationCodeRequestAuthenticationToken
生成OAuth2AuthenticationContext
对象: 是一个储存容器,包含了 Authentication
与可选的额外参数
OAuth2AuthorizationCodeGenerator
负责具体生成 Code
OAuth2AuthorizationService
,将最终生成OAuth2Authorization
保存起来。在获取 Token 时候用。
通过了解源代码,那么有那些地方可以进行定制呢?
例如定制 Login
http.oauth2Login(a->a.)
未完,待续。
OAuth2TokenEndpointFilter
是整个流程的入口。
URl
是否匹配Request
转换authenticate
Response
返回调用RequestMatcher.matches
,URl
是否匹配
调用DelegatingAuthenticationConverter.convert
,将Request
传递过来的内容进行校验与转换。有三种转换情况
OAuth2AuthorizationCodeAuthenticationConverter
转换器
null
2、从SecurityContextHolder
得到clientPrincipal
3、得到code
4、得到redirect_uri
5、得到其他additionalParameters
6、返回OAuth2AuthorizationCodeAuthenticationToken
OAuth2RefreshTokenAuthenticationConverter
转换器
OAuth2ClientCredentialsAuthenticationConverter
转换器
AuthenticationManager
进行授权,这个类要调用具体的子类
OAuth2AuthorizationCodeAuthenticationProvider
,这个类负责得到Token
authorizationCodeAuthentication
authorizationCodeAuthentication.getPrincipal()
得到clientPrincipal
。 如果clientPrincipal.isAuthenticated()
就表示成功。clientPrincipal.getRegisteredClient()
得到注册的registeredClient
Code
,通过authorizationService.findByToken
方法得到OAuth2Authorization:authorization
authorization.getToken
,来得到在请求Code
时,系统保存的Code
,authorizationCode
authorization.getAttribute
得到authorizationRequest
ClientId
不同,或者Code.isInvalidated()
,就抛出错误RedirectUri
不同,就抛出错误!authorizationCode.isActive()
,Code
失效了,就抛出错误tokenContextBuilder
authorizationBuilder
tokenContextBuilder.tokenType
,生成tokenContext
tokenGenerator.generate(tokenContext)
,生成generatedAccessToken
generatedAccessToken
,生成accessToken
generatedAccessToken
类型是ClaimAccessor
,就设置claims
属性public client
或者registeredClient
没有包含refresh_token
,就不执行refreshTokenGenerator
不为空,就用refreshTokenGenerator
生成refreshToken
tokenContext
,然后使用tokenGenerator.generate(tokenContext)
产生新的generatedRefreshToken
authorizationBuilder.refreshToken(refreshToken)
做一下处理。Scope
包含了openid
,就创建一个id_token
类型的tokenContext
tokenGenerator
生成generatedIdToken
,这个generatedIdToken
类型是Jwt
OidcIdToken
形成idToken
authorizationBuilder.token
来对idToken
进行处理authorization = authorizationBuilder.build();
OAuth2AuthenticationProviderUtils.invalidate
将code
做废掉,因为只能用一次authorizationService
保存authorization
idToken
不等空,就得到additionalParameters
OAuth2AccessTokenAuthenticationToken
,里面包含;registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters
sendAccessTokenResponse
,将得到的Token
返回给前台。
在CommonOAuth2Provider
类中定义了例如 google github 等常用的第三方登陆。
{"alg":"RS256"}
{"sub":"user","scope":"message:read","iss":"https://www.example.com","exp":1651805873}
{"typ":"JWT","alg":"RS256"}
{"sub":"subject","iat":1516239022}