文章首发于:github.com/USTB-musion…html
昨天在组内分享了第三方登陆与单点登陆,其中着重分享了第三方登陆当中的oAuth2协议,在这里记录整理一下。oAuth协议是一个受权的开放网络标准,主要是用来解决第三方登陆的,即所谓第三方登陆,实际上就是oAuth的受权。前端
不少网站登陆时,容许使用第三方网站的身份来进行登陆,这称为“第三方登陆”。好比知乎和慕课网等,可使用微信,QQ,或微博来进行登陆。一个网站想接入第三方登陆,须要用到oAuth这个协议。git
oAuth是一个关于受权的开放网络标准,用来受权第三方应用,获取用户的数据。其最终的目的是为了给第三方应用颁发一个有时效性的令牌access_token,第三方应用根据这个access_token就能够去获取用户的相关资源,如头像,昵称,email这些信息。如今你们用的基本是2.0的版本。github
oAuth2.0的这个协议是从RFC 6749这篇文章当中提出来的,若是想了解更全面的信息,能够点击👆这篇文章进行了解。下面来介绍一下oAuth2的角色和流程。web
在详细介绍oAuth2协议流程以前,先来简单了解几个角色,方便后续的理解。docker
了解了上面这些角色以后,来看下oAuth2.0的运行流程是怎么样的。json
+--------+ +---------------+
| |--(A)- Authorization Request ->| Resource |
| | | Owner |
| |<-(B)-- Authorization Grant ---| |
| | +---------------+
| |
| | +---------------+
| |--(C)-- Authorization Grant -->| Authorization |
| Client | | Server |
| |<-(D)----- Access Token -------| |
| | +---------------+
| |
| | +---------------+
| |--(E)----- Access Token ------>| Resource |
| | | Server |
| |<-(F)--- Protected Resource ---| |
+--------+ +---------------+
复制代码
(A). 用户打开客户端(Client),客户端向受权服务器(Resource Owner)发送一个受权请求后端
(B). 用户赞成给客户端(Client)受权浏览器
(C). 客户端使用刚才的受权去向认证服务器(Authorization Server)认证bash
(D). 认证服务器认证经过后,会给客户端发放令牌(Access Token)
(E). 客户端拿着令牌(Access Token),去向资源服务器(Resource Server)申请获取资源
(F). 资源服务器确认令牌以后,给客户端返回受保护的资源(Protected Resource)
在oAuth2当中,定义了四种受权方式,针对不一样的业务场景:
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI ---->| |
| User- | | Authorization |
| Agent -+----(B)-- User authenticates --->| Server |
| | | |
| -+----(C)-- Authorization Code ---<| |
+-|----|---+ +---------------+
| | ^ v
(A) (C) | |
| | | |
^ v | |
+---------+ | |
| |>---(D)-- Authorization Code ---------' |
| Client | & Redirection URI |
| | |
| |<---(E)----- Access Token -------------------'
+---------+ (w/ Optional Refresh Token)
复制代码
Note: The lines illustrating steps (A), (B), and (C) are broken into two parts as they pass through the user-agent.
受权码模式如上图所示,这种流程是功能最完整,流程也是最严密的受权方式,适用于那些有后端的web应用。它的特色是经过客户端的后台服务器和服务商的认证服务器进行通信。它的流程以下,若是我想使用github来接入第三方登陆:
(A). 用户(Resource Owner)在用户代理(User-Agent,如web浏览器,app)上选择了第三方应用(如github)来进行登陆,会重定向到github的受权端点:
https://github.com/login/oauth/authorize?
response_type=code&
client_id=your_code&
redirect_uri=重定向的url&
scope=read&
state=uuid
复制代码
字段 | 描述 |
---|---|
response_type | 必须,在受权码模式中固定为code |
client_id | 必须,惟一标识了客户端,在github注册时得到的客户端ID |
redirect_url | 客户端在github注册的重定向url,用户赞成或拒绝的时候都会跳转到这个重定向url |
scope | 可选,请求资源范围,若有多项,使用多个空格隔开 |
state | 推荐,客户端生成的随机数,资源服务器会原样返回,防止CSRF的攻击 |
(B). 页面跳转后,github会要求用户登陆,而后询问是否给予客户端受权,用户点击赞成。
(C). 而后github就会将受权码(Authorization Code)返回给redirect_uri(重定向uri)。
redirect_uri?code=xxxxxxx
复制代码
字段 | 描述 |
---|---|
code | 必须,受权码 |
state | 防止CSRF攻击的参数 |
(D). 客户端(Client)在经过在URL中取出受权码以后,就能够在后端向github请求令牌
https://github.com/login/oauth/access_token?
client_id=your_code&
client_secret=your_secret&
grant_type=authorization_code&
code=取出的code&
redirect_uri=重定向的url
复制代码
字段 | 描述 |
---|---|
client_id | 必须,客户端在github注册的惟一标识 |
client_secret | 必须,客户端在github注册时返回的密钥 |
grant_type | 必须,authorization_code/refresh_code |
code | 必须,上一步中取出的受权码 |
redirect_uri | 必须,完成受权以后的回调地址,与在github注册时的一致 |
(E). github给redirect_uri指定的地址返回AccessToken,经过JSON格式返回
{
"access_token":"xxxxxxx",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"xxxxxxx"
}
复制代码
客户端就能够在后端取到access_token,在这段json中,还返回了一个refresh_token,这个refresh_token表示用于访问下一次的更新令牌,refresh_token的时效性比access_token长,当access_token过时时,可使用refresh_token换取新的access_token。
简化模式主要针对没有后端的纯前端应用,在这种状况下,由于没有后端,因此就不能采用受权码模式的这种流程了,必需要把access_token存在前端。
+----------+
| Resource |
| Owner |
| |
+----------+
^
|
(B)
+----|-----+ Client Identifier +---------------+
| -+----(A)-- & Redirection URI --->| |
| User- | | Authorization |
| Agent -|----(B)-- User authenticates -->| Server |
| | | |
| |<---(C)--- Redirection URI ----<| |
| | with Access Token +---------------+
| | in Fragment
| | +---------------+
| |----(D)--- Redirection URI ---->| Web-Hosted |
| | without Fragment | Client |
| | | Resource |
| (F) |<---(E)------- Script ---------<| |
| | +---------------+
+-|--------+
| |
(A) (G) Access Token
| |
^ v
+---------+
| |
| Client |
| |
+---------+
复制代码
Note: The lines illustrating steps (A) and (B) are broken into two parts as they pass through the user-agent.
主要是B这个步骤,页面跳转到github网站,用户赞成给予客户端受权。github就会把令牌做为URL参数,跳转回到redirect_uri的这个回调地址。
回调地址#token=xxxxxx
复制代码
注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是由于 OAuth 2.0 容许跳转网址是 HTTP 协议,所以存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减小了泄漏令牌的风险。
若是你高度信任某个应用,RFC 6749 也容许用户把用户名和密码,直接告诉该应用。该应用就使用你的密码,申请令牌,这种方式称为"密码式"(password)。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Figure 5: Resource Owner Password Credentials Flow
复制代码
密码模式就是用户向客户端提供本身的帐号和密码,客户端使用这些信息去向咱们的服务提供商去索要一个受权。
客户端以本身的名义,而不是用户的名义,向“服务提供商”进行认证,如微信公众号以此access_token来拉取全部已关注用户的信息,docker到dockerhub拉取镜像等。
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
Figure 6: Client Credentials Flow
复制代码
客户端模式,顾名思义就是指客户端以本身的名义而不是用户的名义去向服务的提供商去作一个认证,严格来讲,这种模式并非 oAuth 框架要解决的问题,在这种客户端模式下呢,它是直接经过客户端的密钥和id去获取一个access_token的,不须要用户去参与。
那关于 oAuth2 的理解呢,大概介绍这么多的内容。oAuth 协议主要是用来解决第三方登陆的,但聊到不一样场景下的登陆方案时,除了第三方登陆以外,还有一个概念,就是单点登陆。
单点登陆就是在多个系统中,用户只需登陆一次,各个系统就能够感知该用户已经登陆。好比说你登陆了天猫,淘宝也会自动登陆。简单地理解,单点登陆就是这样,它经过将两个或多个产品中的用户登陆逻辑抽离出来,经过只输入一次用户名和密码,就能够达到同时登陆多个产品的效果。
第一种是同一父域下的单点登陆,好比说hr.oa.com,km.oa.com,fuli.oa.com,那这种状况就能够经过将domain属性设置为二级域名oa.com来共享cookie,而后服务端经过共享session就能够实现单点登陆。除了共享session以外固然也能够用JWT这种方式进行实现。
那第二种就是针对不一样域下的单点登陆,好比说淘宝和天猫,它的二级域名是不相同的。这种状况,就要解决cookie不共享的问题。如今主流的方案就是使用cas来实现。
来简单总结一下,针对不一样业务场景下登陆的主流解决方案,第一种是针对同一公司,同一父域下的单点登陆解决方案,这种状况由于cookie是同父域下的,设置cookie的domain属性能够实现cookie共享。而后服务端session共享就能够实现单点登陆。但还有一种这种方式解决方案是JWT。JWT就是json web token。实际上就是一个字符串,由头部,载荷和签名三部分组成。
第二种是针对同一公司,可是不一样域下的单点登陆解决方案,好比说淘宝和天猫的单点登陆,那这种方式的主流解决方案是CAS。
那第三种就是不一样公司,不一样域下的就使用第三方登陆功能实现。如第三方网站须要接入微信登陆,QQ登陆,微博登陆等,那第三方登陆功能的实现呢,就用到刚才介绍的 oAuth2.0 的协议。