文档章节

OAuth2.0协议原理与实现:TOKEN生成算法

zhenchao
 zhenchao
发布于 2017/03/11 23:11
字数 3327
阅读 6307
收藏 5

OAuth2.0协议原理与实现系列


  OAuth2.0协议定义了授权详细流程,并最终以token的形式作为用户授权的凭证下发给客户端,客户端后续可以带着token去请求资源服务器,获取token权限范围内的用户资源。

  对于token的描述,OAuth2.0协议只是一笔带过的说它是一个字符串,用于表示特定的权限、生命周期等,但是却没有明确阐述token的生成策略,以及如何去验证一个token。RFC6749对于access token的描述:

The client obtains an access token -- a string denoting a specific scope, lifetime, and other access attributes.

  协议不去详细阐述token的生成和验证过程,个人觉得是因为这一块各个业务都有自己的特点,无法完全做到抽象,并且在这一块去做详细的规定,其意义并不大。Token本质上就是对用户授权这一操作在时间和权限范围两个维度上的一个表征,协议可以对token的传递和基本验证做相应规定,但是具体的一个token包含哪些元素,采用什么样的生成算法还是需要由自己去把握。

  本文主要讲解自己对于token生成的一些思考,以及介绍两种类型的token:BEARER类型和MAC类型。

一. TOKEN的基本构成

  Token表征了用户授权这一操作,授权服务器通过下发token来给客户端颁发获取用户受保护资源的资格,且不会因此而泄露用户的登录凭证信息。Token对于客户端应该是非透明的,客户端只知道这是一个字符串,能够用它来获取用户的受保护资源,对于字符串内部所含的信息应该无从知晓,也不能通过其它方法去解密其中的信息。所以token应该是一类对称加密得到的字符串,并且只有授权服务器持有对称密钥,用于对生成的token进行加密和验证。

对于构成token的元素,各个业务都有自己的需求,不过仍然存在一些基本通用的元素,比如:

  1. clientId:客户端ID,当前token隶属的客户端

  2. userId:用户的ID,表示当前token来自哪个用户授权

  3. scope: 权限范围,该token允许换取的用户受保护资源范围

  4. issueTime: 下发时间,用于控制token的生命周期

  5. tokenType: token的类型,不同类型可能会采用不同的验证措施

以上是我个人根据经验总结的一些基础的token组成元素,具体业务还可以根据具体的需求添加一些其他的元素。

二. Bearer Type Access Token

  BEARER类型的token是在RFC6750中定义的一种token类型,OAuth2.0协议RFC6749对其也有所提及,算是对RFC6749的一个补充。BEARER类型token是建立在HTTP/1.1版本之上的token类型,需要TLS(Transport Layer Security)提供安全支持,该协议主要规定了BEARER类型token的客户端请求和服务端验证的具体细节。

2.1 客户端请求

  客户端在携带token请求用户的受保护资源时,需要保证token的安全性,以防止token被窃取或篡改,从而损害用户数据安全。BEARER类型token定义了三种token传递策略,客户端在传递token时必须使用其中的一种,且最多一种。

2.1.1 放在Authorization请求首部

Authorization首部说明

Authorization首部是由客户端发送,以向服务器回应自己的身份验证信息,客户端在收到服务器的401 Authentication Required响应之后,需要在请求中包含该首部。

基本用法:Authorization: <authentication-scheme> <authentication-param>

在传输时,Authorization首部的authentication-scheme需要设置为Bearer,请求示例:

GET /resource HTTP/1.1
Host: server.example.com
Authorization: Bearer mF_9.B5f-4.1JqM
2.1.2 放在请求实体中

  Token需放置在access_token参数后面,且Content-Type需要设置为application/x-www-form-urlencoded,请求示例如下:

POST /resource HTTP/1.1
Host: server.example.com
Content-Type: application/x-www-form-urlencoded

access_token=mF_9.B5f-4.1JqM

协议推荐使用第一种方式,对于该请求方式,必须在满足如下条件时才允许使用:

  1. The HTTP request entity-header includes the "Content-Type" header field set to "application/x-www-form-urlencoded".

  2. The entity-body follows the encoding requirements of the "application/x-www-form-urlencoded" content-type as defined by HTML 4.01.

  3. The HTTP request entity-body is single-part.

  4. The content to be encoded in the entity-body MUST consist entirely of ASCII characters.

  5. The HTTP request method is one for which the request-body has defined semantics. In particular, this means that the "GET" method MUST NOT be used.

2.1.3 放在URI请求参数中

  该方式通过在请求URl后面添加access_token参数来传递token,请求示例如下:

GET /resource?access_token=mF_9.B5f-4.1JqM HTTP/1.1
Host: server.example.com

  客户端在请求时需要设置Cache-Control: no-store,服务端在成功响应时也需要设置Cache-Control: private

  由于很多服务都会以日志方式去记录用户的请求,此类方式存在较大的安全隐患,所以一般不推荐使用,除非前两种方案均不可用。

2.2 服务端验证

  如果服务端拒绝客户端的访问请求,则需要在响应中添加WWW-Authenticate首部,响应示例如下:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example"

WWW-Authenticate首部说明

WWW-Authenticate首部用于401 Unauthorized响应,用于向客户端发送一个质询认证方案。

基本用法:WWW-Authenticate:<auth-scheme> <challenge>

  这里的响应,其中auth-scheme必须设置为Bearer,如果客户端携带了无效的token,那么按照上一篇《OAuth2.0协议原理与实现:协议原理》讲解的,OAuth2.0协议要求错误响应中必须携带error字段,并选择性携带error_descriptionerror_uri,具体释义请参考上一篇,响应示例如下:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="example",
                  error="invalid_token",
                  error_description="The access token expired"

三. MAC Type Access Token

  前面介绍了BEARER类型的token,RFC6750明确说明该类型token需要TLS(Transport Layer Security)提供安全支持。虽然现今大部分站点都已经或正在由HTTP向HTTPS迁移,但是仍然会有站点继续在使用HTTP,在这类站点中BEARER类型的token存在安全隐患,这个时候MAC类型的token正是用武之地,MAC类型的token设计的主要目的就是为了应对不可靠的网络环境。

  MAC类型相对于BEARER类型对于用户资源请求的区别在于,BEARER类型只需要携带授权服务器下发的token即可,而对于MAC类型来说,除了携带授权服务器下发的token,客户端还要携带时间戳,nonce,以及在客户端计算得到的mac值等信息,并通过这些额外的信息来保证传输的可靠性。

3.1 下发MAC类型令牌

  OAuth2.0协议在规定下发accessToken时,包含access_tokentoken_typeexpires_inrefresh_token,以及scope字段,其中部分字段可选,具体参见上一篇《OAuth2.0协议原理与实现:协议原理》,示例如下:

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"
}

  响应字段是可扩展的,对于MAC类型token则增加了mac_keymac_algorithm两个字段,mac_key是一个客户端和服务端共享的对称密钥,mac_algorithm则指明了加密算法(比如hmac-sha-1,hmac-sha-256),示例响应如下:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache

{
    "access_token":"SlAV32hkKG",
    "token_type":"mac",
    "expires_in":3600,
    "refresh_token":"8xLOxBtZp8",
    "mac_key":"adijq39jdlaska9asud",
    "mac_algorithm":"hmac-sha-256"
}

3.2 构造MAC类型请求

  一些开放API接口可能会强制要求以MAC类型令牌来请求,这个时候就需要在客户端构造合法的请求,一个标准的请求示例如下:

GET /resource/1?b=1&a=2 HTTP/1.1
Host: example.com
Authorization: MAC id="h480djs93hd8",
                   ts="1336363200",
                   nonce="dj83hs9s",
                   mac="bhCQXTVyfj5cmA9uKkPFx1zeOXM="

请求参数说明:

参数名必须描述信息
id必须访问令牌
ts必须时间戳
nonce必须客户端生成的字符串,对于相同token和timespan的请求nonce必须相同
ext可选扩展信息
mac必须根据MAC key和MAC algorithm计算出来的值

  通过添加id、ts、nonce、mac字段到Authorization请求首部以发起对用户资源的请求,这里的id就是授权服务器下发的accessToken;ts则是时间戳,由客户端生成,以秒为单位;nonce是客户端生成的一个字符串形式的签名,是对ts和id两个维度的唯一、可重复性标识;而mac则是整个客户端构造最核心和复杂的部分,可以看做是对本次请求参数的一个签名,1.2.3小节专门讲解。此外客户端还以用ext字段来携带一些扩展数据。

3.3 mac值算法

  mac值可以看作是对本次请求参数的一个签名,通过对请求数据进行本地加密计算得到,用于防止请求过程中参数被更改。服务器端收到请求之后,会以相同的算法和密钥重新计算一遍mac值,并与客户端传递过来的作比较,如果不一致则拒绝该请求。因为密钥仅保存在客户端和服务端本地,所以无需担心mac值被更改或伪造,从而确保在没有TLS保证的环境下可靠传输,实际上这里可以看做是MAC类型请求自己实现了一遍TLS。

mac值对于相同的请求参数必须是一致和可再计算的,对于参与计算元素的选择,协议选取了如下元素:

  1. The timestamp value calculated for the request.

  2. The nonce value generated for the request.

  3. The HTTP request method in upper case. For example: HEAD, GET, POST, etc.

  4. The HTTP request-URI as defined by RFC2616 section 5.1.2.

  5. The hostname included in the HTTP request using the Host request header field in lower case.

  6. The port as included in the HTTP request using the Host request header field. If the header field does not include a port, the default value for the scheme MUST be used (e.g. 80 for HTTP and 443 for HTTPS).

  7. The value of the ext Authorization request header field attribute if one was included in the request, otherwise, an empty string.

  通过对这些元素按照顺序组织,并以换行符\n作分隔(最后一行也需要包含一个\n),利用mac_algorithm指定的算法和mac_key指定的密钥对组织好的数据进行加密计算得到mac值。

计算示例:

假设有一个请求:

POST /request?b5=%3D%253D&a3=a&c%40=&a2=r%20b&c2&a3=2+q HTTP/1.1
Host: example.com

Hello World!

其中ts=264095:7d8f3e4a,nonce=7d8f3e4a,ext=a,b,c

对该请求按照之前的说明进行组织,以\n分隔得到:

264095\n
7d8f3e4a\n
POST\n
/request?b5=%3D%253D&a3=a&c%40=&a2=r%20b&c2&a3=2+q\n
example.com\n
80\n
a,b,c\n

其中\n仅仅是为了展示,实际中以ASCII码%x0A的意义表示,不要忘了最后一行的\n。假设授权服务器指定的mac_algorithm为hmac-sha-1,令text表示上面的字符串,那么最后的mac值得计算方式如下:

mac = hmac-sha-1(mac_key, text)

3.4 服务端验证

服务器端在收到客户端的请求之后,需要做如下验证:

  1. 重新计算mac值,并与客户端传递的值进行比较

  2. 确保(timestam, nonce, token)三个维度之前没有被请求过,以防止重放攻击

  3. 验证scope,以及token

如果服务端拒绝客户端的请求,则需要指定WWW- Authenticate响应首部,例如客户端携带了无效的授权信息,则服务器响应示例如下:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: MAC error="The MAC credentials expired"

四. 本篇小结

  本篇主要介绍了两种token类型,基本可以覆盖实际应用中的各种场景。Token是对用户授权操作的一类凭证,一旦下发到客户端,其安全性就需要客户端去保证,为了尽量在保护用户数据和提升用户体验上寻找一个平衡点,token的生命周期不应该设置的太短或太长,一般可以以月为单位,并推荐走授权码授权,请求下发刷新令牌,刷新令牌的生成和验证也可以参考本文的两种类型。

  本篇和上一篇《OAuth2.0协议原理与实现:协议原理》介绍了OAuth2.0协议涉及到的理论知识,如果你已经看得手痒了,那么请期待下一篇吧,在下一篇中我将以java语言述写OAuth2.0的具体实现!

文中不免有错误之处,欢迎批评指正,如果您觉得写得还不错,让您有所收获,欢迎打赏~

一分钱也是钱,一分钱也是爱,哈哈~

参考文献

  1. RFC5849 - The OAuth 1.0 Protocol
  2. RFC6749 - The OAuth 2.0 Authorization Framework
  3. RFC6750 - The OAuth 2.0 Authorization Framework: Bearer Token Usage
  4. HTTP Authentication: MAC Authentication (draft-hammer-oauth-v2-mac-token-02)

鉴于作者水平有限,文中不免有错误之处,欢迎大家批评指正~

同步更新,我的个人小站:www.zhenchao.org

© 著作权归作者所有

zhenchao
粉丝 110
博文 28
码字总数 98844
作品 0
海淀
高级程序员
私信 提问
开放平台_OAuth2.0

为什么出现oauth2.0 1 oauth1.0对手机客户端,移动设备等非server第三方的支持不好。其实oauth1.0也是可以支持手机客户端,移动设备等,也有相应的流程。但是oauth1.0是将多种流程合并成了一...

王二狗子11
2018/01/08
0
0
oauth授权协议的原理

http://oauth.net/2/ 协议的原文。原来是1.0版本,现在是2.0版本了 通俗解释: http://www.ruanyifeng.com/blog/2014/05/oauth20.html 要解决的问题: 获取授权。每次登录,都要让用户进行授权...

wangtaotao
2015/06/07
0
0
oauth 开放授权

本想前段时间就把自己通过QQ OAuth1.0、OAuth2.0协议进行验证而实现QQ登录的心得及Demo实例分享给大家,可一直很忙,今天抽点时间说下OAuth1.0协议原理,及讲解下QQ对于Oauth1.0的认证开发。...

bengozhong
2016/08/15
59
1
OAuth的机制原理讲解及开发流程

本想前段时间就把自己通过QQ OAuth1.0、OAuth2.0协议进行验证而实现QQ登录的心得及Demo实例分享给大家,可一直很忙,今天抽点时间说下OAuth1.0协议原理,及讲解下QQ对于Oauth1.0的认证开发。...

只想一个人静一静
2014/04/26
194
0
iOS开发- OAuth2.0认证和SSO授权

iOS开发- OAuth2.0认证和SSO授权 分类: iOS开发2014-11-05 14:21 328人阅读 评论(0) 收藏 举报 OAuth2.0认证和SSO授权微博ios OAuth2.0和SSO授权 一、OAuth2.0授权协议   一种安全的登陆协...

法斗斗
2015/09/24
120
0

没有更多内容

加载失败,请刷新页面

加载更多

maven 环境隔离

解决问题 即 在 resource 文件夹下面 ,新增对应的资源配置文件夹,对应 开发,测试,生产的不同的配置内容 <resources> <resource> <directory>src/main/resources.${deplo......

之渊
今天
8
0
Linux创建yum仓库

第一步、搞定自己的光盘 #创建文件夹 mkdir -p /media/cdrom #挂载光盘 mount /dev/cdrom /media/cdrom #编辑配置文件使其永久生效 vim /etc/fstab 第二步,编辑yun源 vim /ect yum.repos.d...

究极小怪兽zzz
今天
6
0
jar 更新部分文件

C:\Program Files (x86)\Java\jdk1.8.0_102\bin>jar -hIllegal option: hUsage: jar {ctxui}[vfmn0PMe] [jar-file] [manifest-file] [entry-point] [-C dir] files ...Options: -c c......

圣洁之子
今天
9
0
OSChina 周六乱弹 —— 感谢女装红薯开办了这个网站

Osc乱弹歌单(2019)请戳(这里) 【今日歌曲】 @胖达panda:分享歌词: 我有一只小毛驴我从来也不骑,有一天我心血来潮骑着去赶集,我手里拿着小皮鞭我心里正得意,不知怎么哗啦啦,我摔了一...

小小编辑
今天
2.6K
13
DDD(四)

1,引言 软件开发者大多趋向于将关注点放在数据上,而不是领域上。这对于刚入门的DDD的新手而言也是如此。以我目前的思考方式,数据库依然占据主要的地位。开发一个功能,首先我就会考虑我会...

MrYuZixian
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部