理解OAuth2.0

简介

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 可选 客户端自生成的随机码,客户端请求授权服务器授权需要携带该参数,授权成功重定向到客户端时也要把该参数传回。

考虑以下几个问题:

  1. 为什么需要code,直接返回accessToken不是更简单么?
    重定向是不安全的信道,授权服务器授权成功后重定向到客户端,此时accessToken是暴露在外面的,如果此时accessToken被非法获取(比如跨站脚本xss),就会导致用户信息泄露。而code需要和client_secret一起传给授权服务器,从而获取到accessToken,即便code暴露也不会影响。
  2. 既然有了client_id,为什么还需要client_secret?
    客户端通过重定向的方式到授权服务器请求授权码code,此时client_id是暴露在外面,授权服务器授权成功后重定向到客户端,此时code是暴露在外面的,如果此时code、client_id被非法获取(比如跨站脚本xss),意味着accessToken就可轻易被获取,从而导致用户信息泄露。因此客户端请求授权服务器获取accessToken需要传client_secret,授权服务器验证client_secret的合法性。client_secret一般会存储在客户端的后端服务器,不存在暴露的风险。
  3. 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。

你可能感兴趣的:(理解OAuth2.0)