OAuth 2.0 授权模式

OAuth 2.0 是什么?

OAuth 2.0 是一个授权的开放网络标准

角色

Resource Owner:资源所有者,既用户
User Agent:用户代理
Authorization Server:既提供第三方登录服务的服务器
Client:第三方应用,我们的应用就是一个client

客户端的授权模式类型

  1. 授权码模式
  2. 隐式授权模式
  3. 密码模式
  4. 客户端模式

授权码模式

 +----------+
 | 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)

1.授权请求
比如你想登录gitlab,并且想利用三方授权登录,比如谷歌账号,当你点击谷歌图标的时候,会首先发起一个请求,会带上以下参数,向授权服务器发送授权请求:

response_type:表示授权类型,必选项
client_id:表示客户端的ID,必选项,一般是在授权服务器上申请应用的时候,颁发的
redirect_uri:重定向的 uri
scope:表示申请的权限范围
state:表示客户端的当前状态,可以是任意值,认证服务器会原封不动的返回这个值

下面是一个例子:

GET /authorize?response_type=code&client_id=s6BhdRkqt3&state=xyz
        &redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

授权服务器验证该请求,确保所有的参数提交且有效,授权服务器会引导用户进入授权页面

2.当用户点击确定授权时,授权服务器会返回授权码(code)和状态参数(state),返回请求到相应的回调地址(redirect_uri)。至此,用户的主动行为已经结束。
3.第三方应用,也就是我们的客户端,在拿到授权码之后会直接向授权服务器请求访问令牌,参数如下:

grant_type:授权模式,必选项,值  "authorization_code"
code:从授权服务器收到的授权码
redirect_uri:回调地址
client_id:标识应用ID
client_secret:授权服务器颁发的密钥

授权服务器验证通过后会返回如下参数:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

至此整个授权码流程结束。

需要注意的几个问题
  • 重定向 redirect_uri uri 整个授权过程都是一样的

  • 授权过程只是授权:
    比如,你想用 "支付宝" 授权登录 "优酷" ,整个授权的过程只是支付宝验证用户授权,返回一个 openId 和 支付宝允许给优酷的一些用户信息,优化拿到 openId可以直接上传服务端注册,至此整个授权流程结束。后面优酷相关联的资源获取和授权没有任何关联了。优酷会对这个 openId和账号做唯一映射,这样下次还用支付宝授权登录,优酷的服务端不会在创建新的账号给该用户,即不需要用户密码的一键登录。

  • 为什么整个授权流程不直接返回 access_token 而是要经过中间步骤先要获取授权码 code ,在根据授权码再次获取 access_token ?
    最直接的答案是安全,code有效期比较短,而且一个code只能换取一次access_token即失效。获取code的请求是用户主动授权之后获取的从这里截止,用户的主动行为结束。
    获取参数不包含 appsecret,请求回来的code回立马再次发起请求,这次请求会带上appsecret,授权服务器验证并返回 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 |
 |         |
 +---------+

和 授权码请求一样,首先你会发起一个授权请求,验证参数

  1. 授权请求:
response_type:表示授权类型,此处固定值为 token
client_id:客户端标识
redirect_uri:重定向 uri
scope:访问的权限范围
state:表示客户端的当前状态,可以是任意值,认证服务器会原封不动的返回

客户端发起 HTTP 请求,如下:

GET /authorize?response_type=token&client_id=s6BhdRkqt3&state=xyz&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb HTTP/1.1
Host: server.example.com

2.授权服务器会验证该请求,确保所有的参数已提交且有效。授权服务器验证请求参数中的uri和客户端提交的重定向的uri保持一致。
如果,该请求是有效的,授权服务器验证对资源所有者进行身份验证并展示授权页面给用户。
用户同意授权请求时,授权服务器回返回如下参数:

access_token:授权服务器颁发的访问令牌
token_type:令牌类型
expires_in:过期时间
scope:表示授权权限范围
state:表示客户端的当前状态,可以是任意值,认证服务器会原封不动的返回这个值

这些参数,拼接在重定向 uri 地址的后面

3.第三方应用向资源服务器发起请求,不包含上一步获取的access_token
资源服务器返回一个网页(通常是带有嵌入式脚本的 HTML 文档),其中的脚本可以提取出令牌,浏览器把access_token发送给客户端
至此整个简单授权模式结束。

隐式授权和授权码授权的区别就是,是直接获取access_token,不需要经过code步骤。但是获取的access_token需要资源服务器的脚本从第三方代理(既浏览器)中提取出来)发送给客户端。

密码模式

 +----------+
 | Resource |
 |  Owner   |
 |          |
 +----------+
      v
      |    Resource Owner
     (A) Password Credentials
      |
      v
 +---------+                                  +---------------+
 |         |>--(B)---- Resource Owner ------->|               |
 |         |         Password Credentials     | Authorization |
 | Client  |                                  |     Server    |
 |         |<--(C)---- Access Token ---------<|               |
 |         |    (w/ Optional Refresh Token)   |               |
 +---------+                                  +---------------+

这种模式用户必须把"用户名"和"密码"给到客户端,客户端不得存储密码。这通常用在用户读客户端高度信任的情况下。

  1. 资源所有者像客户端提供他的用户名和密码
  2. 当发起请求客户端向授权服务器进行身份认证
  3. 授权服务器对客户端进行身份验证,验证资源所有者的凭证,如果有效则颁发访问令牌。

访问令牌请求参数:

grant_type:值必须为 "password"
username:资源所有者用户名
password:资源所有者密码
scope:客户端授权请求范围

例如:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=password&username=johndoe&password=A3ddj3w

授权服务器验证授权请求通过,参数如下:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600,
  "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA",
  "example_parameter":"example_value"
}

客户端模式

 +---------+                                  +---------------+
 |         |                                  |               |
 |         |>--(A)- Client Authentication --->| Authorization |
 | Client  |                                  |     Server    |
 |         |<--(B)---- Access Token ---------<|               |
 |         |                                  |               |
 +---------+                                  +---------------+

其实,这种模式不属于授权模式,是客户端的行为,而非用户行为
1.客户端向授权服务器请求验证,并要求一个访问令牌
2.授权服务器向客户端进行身份验证,如果有效,则颁发一个访问令牌

访问令牌请求:

grant_type:表示授权类型,值必须为 "client_credentials"
scope:客户端的授权请求反问

如下,请求:

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=client_credentials

授权服务器验证该请求,如果请求有效则返回,令牌:

HTTP/1.1 200 OK
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
{
  "access_token":"2YotnFZFEjr1zCsicMWpAA",
  "token_type":"example",
  "expires_in":3600, "example_parameter":"example_value"
}

刷新访问令牌

如果,授权服务器颁发了刷新 refresh_token,可以在客户端发起刷新 access_token,一般 refresh_token有效期是大于access_token有效期
请求参数如下:

grant_type:授权类型,此处必须为 "refresh_token"
refresh_token:颁发给客户端的刷新令牌
scope:表示可访问的权限范围

如下 HTTP 请求

POST /token HTTP/1.1
Host: server.example.com
Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
Content-Type: application/x-www-form-urlencoded
grant_type=refresh_token&refresh_token=tGzv3JOkF0XG5Qx2TlKWIA

参考链接

http://www.rfcreader.com/#rfc6749

你可能感兴趣的:(OAuth 2.0 授权模式)