委托认证就是将CAS对接到第三方服务进行认证,但需要三方认证流程符合规范的流程(OAuth、OIDC),CAS已经提供了现成的github、Facebook、QQ等的对接
我们在使用委托认证的时候,会发现cas集成了N多种client(实际是 pac4j 提供的),比如现成的服务提供商Client:FacebookClient、GithubClient,基于协议的client:OAuthClient、OidcClient、SAML2Client等等,这些client包含了整个认证流程所需的组件,包括:如何跳转到认证URL、提取认证信息credentials、提取用户信息、如何跳转到登录URL这些主要流程,还包括其他组件,比如对获取到的用户属性进行重命名,使其符合我们之前配置过的属性名;
数据结构
我们先来看一下OAuth20Client内部数据结构涉及到的类型:
- OAuth20Client:OAuth20客户端,核心类
- Pac4jOAuth20ClientProperties:定义通用属性:各种url啦、appid、secret啦、需要保留的profile属性啦(可以定义别名),跳转到授权URL时要加的额外参数啦(这个不太受用)
- OAuth20Service:OAuth相关流程业务类,比如请求access_token、refresh_token、设置response_type等等
- DefaultApi20:通过继承该类,定义请求URL,定义签名类型(authorization是放在请求头还是请求参数),以及创建 Service
- OAuth20ProfileDefinition:profile定义,用于生成profile,将授权之后获取到的用户信息设置到profile里
- OAuth20ProfileCreator:profile创建类,包含请求profile时的参数签名,根据access_token获取profile啦这些。。。
- OAuth20Configuration:定义 key、secret、response_type等等,也定义了api、profile definition
其他:
- OAuth20RedirectActionBuilder:重定向行为构造器
- OAuth20CredentialsExtractor:credential提取器
- OAuth20Authenticator:认证类
- OAuth20ProfileCreator:用户信息创建类
- OAuth2AccessExtractor:access_token提取器,还有一个 OAuth2AccessJsonExtractor
认证流程
下面进入正题——认证流程:
首先是实例化入口:org.apereo.cas.support.pac4j.authentication.DelegatedClientFactory#build,通过构造函数初始化各种客户端:GithubClient, FacebookClient, OAuth20Client,通过配置的 properties 实例化对应的 client;
除了上面说的通过构造函数初始化,每个client包含一个init()函数,用来初始化其他字段,比如:
- OAuth20RedirectActionBuilder:重定向行为构造器,用来构造重定向URL,如果需要修改授权URL的参数,需要自定义该方法
- OAuth20CredentialsExtractor:credential提取器,在认证成功之后,要提取code;比如在开发企业微信二维码登录的时候,我暂时用不到code,要暂时保存下来,在后面使用,那就要自定义一个提取器;
- OAuth20Authenticator:认证类,用code来换取access_token;但在qywx中,是不需要code的,而是拿appid+secret来获取,也要自定义;
- OAuth20ProfileCreator:获取用户信息,正常流程是:access_token → user profile,qywx中是:code + access_token → user profile,所以还要自定义
解决暂时保留code 的问题,我在项目中定义了一个ThreadLocal来暂时保存code,而且是一次性的,只要get之后,立马remove
init()方法在什么地方调用呢?我们在测试过程中发现,当第一次使用该方式登录时,会进行调用init,有点懒加载的意思,感觉没必要,其实可以放在构造函数里面;
init()调用流程(这里以 GithubClient 为例):
org.apereo.cas.support.oauth.web.endpoints.OAuth20CallbackAuthorizeEndpointController#handleRequest
|
org.pac4j.core.engine.DefaultCallbackLogic#perform
|
org.pac4j.core.client.finder.DefaultCallbackClientFinder#find
|
org.pac4j.core.util.InitializableObject#init
|
org.pac4j.core.client.IndirectClient#internalInit
|
org.pac4j.oauth.client.GitHubClient#clientInit
延伸
最后插播一下企业微信的授权流程和OAuth授权流程的对比:
授权 | 获取access_token | 获取profile | |
---|---|---|---|
企业微信 | 拿到code | appid + secret → access_token | code + access_token → profile |
OAuth | 拿到code | code → access_token | access_token → profile |