简介
OAuth2.0是一个开放、授权的行业标准协议。广泛用于某些知名网站(例如QQ、微信、微博等)授权第三方应用(比如:自己搭建的应用),以获取知名网站用户的个人信息,昵称、性别、头像等。因此,对于第三方应用来说,无需存储用户相关账号密码等信息;对于用户访问来说,省去注册用户等繁琐步骤。
需要注意的是 OAuth 2.0用于是授权,而不是认证。
几个重要角色
- Client:指第三方应用,试图访问受保护资源的应用程序;
- Resource Owner:资源所有者,能够授予对受保护资源的访问权限的实体,简单理解为:人;
- Authorization Server:授权服务,对资源所有者进行身份验证,成功后,向客户端颁发accessToken;
- Resource Server:资源服务,存储受保护资源的服务器,客户端通过accessToken请求获取受保护的资源。
认证流程
+--------+ +---------------+
| |--(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 ---| |
+--------+ +---------------+
上图抽象了OAuth 2.0流程,四个角色之间的交互,包括以下步骤:
- (A):客户端请求资源所有者的授权。举个例子,当你打开(客户端)登录时,可以看到登录页下方的社交账号登录(授权服务),请求“你”(资源所有者)授权;
- (B):客户端收到资源所有者的许可。比如,当在社交账号登录列表,你点击QQ登录时,会进入客户端;
- (C):客户端向授权服务器请求accessToken,并出示授权许可。
- (D):授权服务器对客户端进行身份验证,并校验授权许可,如果有效,则颁发accessToken。
- (E):客户端通过accessToken请求资源服务器获取受保护资源。
- (F):资源服务器校验客户端的accessToken,如果有效,返回受保护资源。
授权模式
Authorization Code Grant:即授权码模式,作为OAuth2.0最常使用的模式。
+----------+
| 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)
(A):客户端引导资源所有者使用user-agent(比如浏览器)作为授权终端。通过重定向方式,将响应类型、客户端标识符、重定向Uri
、请求的作用域、本地状态发送给授权服务器(例如:https://authorization-server.com/auth?response_type=code&client_id=CLIENT_ID&redirect_uri=REDIRECT_URI&scope=photos&state=1234zyx
),授权服务器可自行验证,是否授予(或拒绝)访问权限。(B):授权服务器通过user-agent对资源所有者进行身份验证,并确定资源所有者是否授予或拒绝访问请求。比如,用户在浏览器提交账号密码到授权服务器成功后,选择授权或者拒绝本次请求。
(C):假设资源所有者授予访问权限,则授权服务器生成code(授权码),并将code和客户端传过来的state重定向到客户端传过来的redirect_uri(例如:
https://example-app.com/cb?code=AUTH_CODE_HERE&state=1234zyx
)。(D):客户端通过code向授权服务器请求accessToken(例如:
POST https://api.authorization-server.com/token grant_type=authorization_code& code=AUTH_CODE_HERE& redirect_uri=REDIRECT_URI& client_id=CLIENT_ID& client_secret=CLIENT_SECRET
)。(E):授权服务器对客户端进行身份验证,验证授权码,并确保重定向URI收到与步骤(C)用于重定向客户端的URI匹配。 如果有效,授权服务器将颁发accessToken和(可选)refreshToken。
几个重要的参数:
参数 | 必传 | 描述 |
---|---|---|
response_type | 是 | 代表本次请求需要返回的数据类型,必须设置成code |
client_id | 是 | 代表客户端的id,由授权服务器生成 |
client_secret | 是 | 代表客户端的秘钥,由授权服务器生成,获取accessToken请求时必传; |
redirect_uri | 可选 | 授权服务器授权成功后需要重定向的地址(一般设置成客户端的uri),请求accessToken时需要传的相同重定向uri; |
scope | 可选 | 请求的范围,指客户端请求授权服务器授权后,可以拥有哪些权限; |
code | 是 | 授权服务器授权成功后,重定向到客户端需要携带该参数,客户端通过该参数去获取accessToken |
state | 可选 | 客户端自生成的随机码,客户端请求授权服务器授权需要携带该参数,授权成功重定向到客户端时也要把该参数传回。 |
考虑以下几个问题:
- 为什么需要code,直接返回accessToken不是更简单么?
重定向是不安全的信道,授权服务器授权成功后重定向到客户端,此时accessToken是暴露在外面的,如果此时accessToken被非法获取(比如跨站脚本xss),就会导致用户信息泄露。而code需要和client_secret一起传给授权服务器,从而获取到accessToken,即便code暴露也不会影响。- 既然有了client_id,为什么还需要client_secret?
客户端通过重定向的方式到授权服务器请求授权码code,此时client_id是暴露在外面,授权服务器授权成功后重定向到客户端,此时code是暴露在外面的,如果此时code、client_id被非法获取(比如跨站脚本xss),意味着accessToken就可轻易被获取,从而导致用户信息泄露。因此客户端请求授权服务器获取accessToken需要传client_secret,授权服务器验证client_secret的合法性。client_secret一般会存储在客户端的后端服务器,不存在暴露的风险。- state是干嘛用的?
主要用于防止csrf。举个例子来说:假设有网站A(客户端)、微信登录(授权服务器)、QQ(授权服务器)、小明(用户)、攻击者(用户);
- 攻击者访问网站A,重定向到微信登录,授权后返回code重定向到网站A时,被攻击者阻断;
- 攻击者将返回的code拼接到网站A的链接发布到自己搭建的非法网站,引诱受害者点击;
- 小明已经通过QQ登录了网站A,只是没有把自己的账号和其他社交账号绑定起来。一会不小心点击了攻击者发布的链接,导致登录了攻击者账号;
- 假设该操作导致了网站A把小明账号跟攻击者账号进行了绑定(不同社交账号进行绑定);
- 之后,攻击者使用自己账号登录,可以直接操作小明的账号数据,比如转账等。
有了state就能很好解决上述问题,首先攻击者登录微信前在网站A生成state,并存储到session中,微信授权后,将code、state一并返回,阻断后将链接发布到网上。小明点击了该链接,网站A判断state与当前session不匹配,则本次登录失败。
Implicit Grant:即隐式模式,通常在浏览器中使用脚本语言实现,例如JavaScript。
+----------+
| 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 |
| |
+---------+
(A):客户端引导资源所有者使用user-agent(比如浏览器)作为授权终端。通过重定向方式,将响应类型、客户端标识符、重定向Uri
、请求的作用域、本地状态发送给授权服务器(例如:https://authorization-server.com/auth ?response_type=token &client_id=29352910282374239857 &redirect_uri=https%3A%2F%2Fexample-app.com%2Fcallback &scope=create+delete &state=xcoiv98y3md22vwsuye3kch
),授权服务器可自行验证,是否授予(或拒绝)访问权限。(B):授权服务器通过user-agent对资源所有者进行身份验证,并确定资源所有者是否授予或拒绝访问请求。比如,用户在浏览器提交账号密码到授权服务器成功后,选择授权或者拒绝本次请求。
(C):假设资源所有者授予访问权限,则授权服务器生成token,并将token和客户端传过来的state重定向到客户端传过来的redirect_uri(例如:
https://example-app.com/redirect#access_token=g0ZGZmNj4mOWIjNTk2Pw1Tk4ZTYyZGI3&token_type=Bearer&expires_in=600&state=xcoVv98y2kd44vuqwye3kcq
)。(D):user-agent遵循重定向指令到web-hosted客户端资源。
(E):web-hosted客户端资源返回一个网页(通常是带有嵌入脚本的HTML文档),并从瞄点提取accessToken(和其他参数)。
(F):user-agent执行web-hosted客户端资源的本地脚本,提取accessToken。
(G):user-agent将accessToken传递给客户端。
隐式模式的主要缺点是accessToken直接在URL中返回,而不是像授权码模式那样通过受信任的返回通道返回。 accessToken本身将被记录在浏览器的历史记录中,因此大多数服务器都会发布短期的accessToken,以减轻accessToken被泄露的风险。 因为没有反向通道,所以隐式流也不会返回refreshToken。
Resource Owner Password Credentials Grant:即资源所有者密码凭证模式,适用于高度信任的第三方应用,账号密码会被第三方应用知道,暴露风险很大。
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(A) Password Credentials
|
v
+---------+ +---------------+
| |>--(B)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(C)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
- (A):资源所有者向客户端提供其用户名和密码。
- (B):客户端请求授权服务器获取accessToken。
POST /oauth/token HTTP/1.1
Host: authorization-server.com
Content-type: application/x-www-form-urlencoded
grant_type=password
&username=exampleuser
&password=1234luggage
&client_id=xxxxxxxxxx
- (C):授权服务器对客户端进行身份验证,如果有效,则颁发accessToken和(可选)refreshToken。
Client Credentials Grant:即客户端凭证模式,是以客户端应用程序维度请求accessToken,而不是用户维度维度请求accessToken。
+---------+ +---------------+
| | | |
| |>--(A)- Client Authentication --->| Authorization |
| Client | | Server |
| |<--(B)---- Access Token ---------<| |
| | | |
+---------+ +---------------+
- (A):客户端通过授权服务器进行身份验证,请求accessToken。
POST /token HTTP/1.1
Host: authorization-server.com
grant_type=client_credentials
&client_id=xxxxxxxxxx
&client_secret=xxxxxxxxxx
- (B):授权服务器对客户端进行身份验证,如果有效,颁发accessToken。