OAuth 2.0 标准系列学习二:鉴权流程以及基本概念

前言

这是笔者在日常工作学习中所总结的有关 OAuth 2.0 的系列文章之一;

OAuth 的标准所涵盖的概念较多,应用场景也较为复杂,所以,笔者打算撰写这样的一系列文章,来总结自己在日常工作和学习中所掌握的 OAuth 2.0 的相关概念和实践;

本文为作者的原创作品,转载需注明出处;

OAuth 2.0 鉴权流程概述

OAuth 2.0 鉴权流程概括

上图描述了 Resource Owner 既用户如何通过 OAuth 协议使得 Client 获取用户在 Resource Server 上的被保护资源的一般流程;

步骤 (A)

Client 试图获取 Resource Owner 被保护资源的时候,将会向 Resource Owner 请求授权,注意,该请求 Resource Owner 进行授权的方式有多种,笔者在授权方式中一一有所介绍;这里,笔者就以最常用的方式,既是 Authorization Code 的方式在这里来做一个简单的描述,Client 通过将 Resource Owner 重定向到 Authorization Server 上后,Resource Owner 通过输入自己的用户名和密码来验证该授权动作,若授权通过,Authorization Server 将通过 Redirect URL 的方式将 Authorization Code 反馈给 Client;其余三种获取授权的方式将在授权方式中进行更为详细的描述;

步骤 (B)

Client 获得由步骤 (A) 最终执行所获得的 Authorization Code

步骤 (C)

Client 通过之前所获取的 Authorization Code 试图向 Authorization Server 获取 Access Token;Authorization Server 将对 Authorization Code 的有效性进行验证,如果通过,则向 Client 颁发 Access Token;一般而言,无论成功与否,该 Authorization Code 都只能被使用一次;

步骤 (D)

若步骤 (C) 对 Authorization Code 的有效性验证成功,这里将会返回 Access Token 给 Client;

步骤 (E)

Client 试图通过从步骤 (D) 所获取得到的 Access Token 来获取 Resource Owner 在 Resource Server 中的被保护资源;注意,Resource Server 不但需要验证 Access Token 的有效性,同时需要根据 Access Token 的中所描述的资源范围和资源访问方式对资源进行限制性访问;

步骤 (F)

返回 Resource Owner 在 Resource Server 中的被保护资源;

授权方式( Grant Type )

该部分内容是对 OAuth 2.0 鉴权流程 中的 步骤 (A) 进行扩展性描述,由上述流程分析可知,步骤 (A) 的作用既是向 Authorization Server 申请并获得 Resource Owner 的授权,其目的最终都是通过该授权而获得 Access Token;归纳起来总共有如下的四种方式;这里仅仅是对这四种方式做简要的描述,后续会对这四种方式依次做更为详细的描述;

Authorization Code

通过将 Authorization Server 作为中间的“媒介”;Client 将 Resource Owner 重定向到 Authorization Server 上进行验证,Resource Owner 输入用户名和密码,经过 Authorization Server 的身份认证,认证成功以后,通常有两种方式将 Authorization Code 反馈给 Client,

  • 通过事先 Client 与 Authorization Server 约定好的 Redirect URL 将 Authorization Code 反馈给 Client;
  • 通过事先 Client 与 Authorization Server 约定好的秘密通道,比如 WebService 调用接口,或者是 TCP 协议等,不经过用户浏览器(以免被劫持)直接将 Authorization Code 反馈给 Client;

Client 在获取得到 Authorization Code 以后,使用 Authorization Code 向 Authorization Server 申请获取访问被保护资源的 Access Token;从上述过程中,可以看到,Authorization Server 在 Client 与 Resource Owner 之间从始至终扮演的都是一个“媒介”的角色,通过“媒介”所产生的中间授权码既 Authorization Code 为 Resource Owner 进行授权,并使得 Client 最终获取得到访问 Resource Owner 被保护资源的访问权限既 Access Token;这样,整个过程中,Client 永远不知道用户的用户名和密码了;

这种方式是最常见也是最为常用的一种方式;

Implicit

该授权方式是专门为 Client 是一个使用 Javascript 实现其逻辑的纯的 Browser 浏览器的情况之下所设计的,也就是说,该 Client 没有后端的应用服务而就是几个简单的纯网页的情况;想想,这种应用场景实际上蛮多的,比如我现在所用到的“畅言”;Implicit 模式不需要“媒介”既 Authorization Code,而是直接获取得到 Access Code;整个流程大致归纳总结如下,

  • Client 将 Resource Owner 重定向到 Authorization Server 上 (参数 grant_type=implicit & client_id=xxxxxx ),并设置 Redirect URL (可选),也可以事先在 Authorization Server 中对该跳转关系进行预先配置;
  • Resource Owner 在 Authorization Server 上进行认证;
  • 认证通过以后,Authorization Server 将会根据与 Client 事先约定好的 Redirect URL 将 Access Token 直接反馈给 Client;

注意,整个过程中,Client 是无需被验证的;而且这个也是必要的,因为在此种情况下,Client 是 Public 类型的客户端,就是说,它的源码是公之于众的,如果仍然需要对 Client 进行验证,那么势必需要知道 Client 的密码( Secret ),而 Client 不具备私密性,那么其密码必然随其源码而公之于众,这样,就导致了 Client Secret 被泄露的极大风险;也正是因为如此,Client 在 Implicit 模式的设计之下是不需要经过验证的;

注意,这里有一个安全问题值得思考,就是 Client ID 是公之于众的,那么如果有其它恶意的 Client(比如钓鱼网站,和你的网站一模一样的) 想冒用你的 Client ID 来获取非法的资源,该怎么办呢?虽然,钓鱼网站可以冒用你的 Client ID,但是它无法冒用你的 Redirect URL,也就是说,即便是对方冒用了你的 Client ID,最终,验证通过以后,Access Token 仍然是发送到你之前与 Authorization Server 所约定好的 Redirect URL 上的,也就是说,只要是使用你自己的 Client ID 进行的授权认证,那么都只有你自己可以接收到到 Access Token,而无论你的 Client ID 是否被它人所冒用;所以,实际上 Implicit 模式下,保证 Access Token 安全最核心的部分其实是 Redirect URL,然后才是 Resource Owner 的用户名和密码;

Resource Owner Password Credentials

这种方式适用于这样的一种场景,既是 Client 本身是整个系统的一部分,是被 Resource Server 所高度信任的,并且,无法通过使用 Client 重定向用户到 Authorization Server 去做验证并获取 Authorization Code 的这样一种应用场景中;典型的就是 APP 应用,它虽然是手机端的一个应用,但是实际上它仍然是整个系统的一部分,并且是属于高度可信的部分;

那么这种模式,Client 是如何获得 Access Token 的呢?Resource Owner 直接使用 Client 输入自己的用户名和密码,然后 Client 将 Resource Owner 的用户名、密码以及 Client 自身的 Client ID 和密码一并发送给 Authorization Server 进行验证,Authorization Server 验证通过后,将会直接返回 Access Token 给 Client;可以看到,整个过程中,不再需要“媒介”既 Authorization Code 而直接获得 Access Token;

不过使用这种方式,仍然要注意账户的安全性;如果 Client 长期的将用户的用户名和密码保存在客户端的本地中,是不安全的,那么如何避免这种情况呢?也很简单,通常是通过一个长期有效的 Access Token 和 Refresh Token 来避免将用户的用户名和密码存储在 Client 的本地;

Client Credentials

这种授权的方式非常的简单,不再需要 Resource Owner 进行授权了;而是直接使用 Client Credentials ( Client ID and Client Secret ) 直接获取得到 Access Token;笔者现在还没有想到一个实际的应用场景或者一个应用,不过能想到的是,可以通过这种方式来防盗链,如果某些资源只需要针对 Client 进行授权的,那么可以使用这种方式;笔者目前能想到的应用场景是,如果需要和第三方系统进行合作(只是与第三方应用的 Client 进行合作而无需与其用户进行合作的场景),那么这个时候,Client 实际上充当的就是 Resource Owner 的这样一个角色;

Client

注册 Client

Client Types

Client ID

验证 Client

Client Password

Other Authentication Methods

Access Token

Access Token 既是用来获取 Resource Owner 的被保护资源的密匙;该密匙通常是由 Authorization Server 随机所生成的字符串,当 Resournce Owner 的授权请求被 Authorization Server 通过以后,Authorization Server 将通过某种方式将 Access Token 返回给 Client

Access Token 还包含了对被保护资源的访问的方式(只读、读写等方式),访问的范围以及被访问的有效时长,这些信息可以通过编码的方式保存在 Access Token 中,每次 Client 提交 Access Token 的时候,服务器将会对其进行验证,不过更有效的方式是可以直接通过配置的方式将这些重要信息保存在服务器上;

使用 Access Token 访问私有资源

Client 向 Resource Server 出示 Access Token 来获取 Resource Owner 的被保护资源;Resource Server 必须要能够对该 Access Token 进行验证,确保它没有失效,并且保证它的访问范围没有超过被请求资源的范围;该请求过程中,Access Token 是使用 HTTP Header 通过使用特殊的格式 ( Access Token Type ) 来进行传输的,具体参考下一小节内容;

Access Token Types

Access Token 主要通过两种类型方式将自己发送给 Resource Server,

  • Bearer
    这种方式,只需要在通过如下的方式将 Access Token 包含在其头部即可,

    1
    2
    3
    GET /resource/1 HTTP/1.1
    Host: example.com
    Authorization: Bearer mF_9.B5f-4.1JqM
  • MAC ( Message Authentication Code )

    1
    2
    3
    4
    5
    GET /resource/1 HTTP/1.1
    Host: example.com
    Authorization: MAC id="h480djs93hd8",
    nonce="274312:dj83hs9s",
    mac="kDZvddkndxvhGRXZhvuDjEWhGeE="

这里可以对比一下的是,Client Credentials 的传递过程是通过 Http Basic 的方式进行的;

Error Response

参考 https://tools.ietf.org/html/rfc6749#section-7.2

Refresh Token

Refresh Token 使用来获取 Access Token 的密匙;同样,是由 Authorization Server 随机所生成的一串字符串;作用如下

  • 当当前的 Access Token 失效以后,通过 Refresh Token 可以直接从 Authorizaiton Server 中再次获得有效的密匙;这样,可以简化授权的流程,同时可以尽最大限度的保证用户的账户信息的安全性,特别是在使用 Resource Owner Password Credentials 的授权模式的情况中;
  • 另外,通过使用 Refresh Token 的方式可以获得更受限制的 Access Token,因为并非是通过 Resource Owner 直接授权的方式,所以,在通过 Refresh Token 的方式获取 Access Token 的时候,可以对该 Access Token 的访问做进一步的限制,进而保证用户的被保护资源的最大安全性;

注意,Refresh Token 对于 Authorization Server 来说,是可选的,也就是说,Refresh Token 不是必须的,如果 Authorization Server 不打算实现或对外发布 Refresh Token,那么 Client 是无法获得 Refresh Token 的;注意,Refresh Token 仅仅作用在 Authorization Server 上用来获取 Access Token,而无需向 Resource Server 转发;下面,我们来看看如何通过 Refresh Token 来获取 Access Token 的流程,

通过 Refresh Token 获取 Access Token 流程

如图步骤 (A)(B)(C)(D) 描绘了正常的授权的流程,既是获取 Authorization Code 和 Access Token 以及 Refresh Token 的流程,以及如何通过 Access Token 来获取被保护资源的流程;从步骤 (E) 开始,Client 发现 Access Token 已经失效,这个时候,将会跳转到步骤 (G),Client 通过使用 Refresh Token 向 Authorization Server 申请一个新的 Access Token,如果 Refresh Token 被验证是有效的,那么通过步骤 (H) Authorization Server 将会返回一个新的 Access Token 和一个可选的( Opational )新的 Refresh Token 给客户端;

TLS 加密传输的必要性

通常情况下因为 Authoriaztion Code 和 Access Token 是通过 HTTP 在网络中进行明文传输的,所以,必须使用 TLS 对整个 HTTP 的通讯链路进行加密(既使用 HTTPS )保证密钥的安全性;如果是通过其他的通讯方式,也必须使用 TLS 保证通讯链路的安全性;