彻底弄懂OAuth

上官锦
2023-12-01


OAuth简介
OAuth是一个开放的标准,能提供一种安全的API授权,使第三方应用不需要密码账号,就可以申请获得该用户资源的授权。

1.使用场景例子
如果一个用户需要两项服务:图片在线存储服务A、图片在线打印服务B。由于服务A与服务B是由两家不同的服务提供商提供的,所以用户在这两家服务提供商的网站上各自注册了两个用户,假设这两个用户名、密码都各不相同。当用户要使用服务B打印存储在服务A上的图片时,用户该如何处理?

方法一:用户先将待打印的图片从服务A上下载下来并上传到服务B上打印,这种方式安全但处理比较繁琐,效率低下;

方法二:用户将在服务A上注册的用户名与密码提供给服务B,服务B使用用户的账号再去服务A处下载待打印的图片,这种方式效率是提高了,但是安全性大大降低了,服务B可以使用用户的用户名与密码去服务A上查看甚至篡改用户的资源。

显然,这两种方法都有缺点,因此需要一个安全的授权机制,让服务B在不获取用户的用户名和密码的情况下得到用户的授权,这就是OAuth的产生背景。在通过OAuth完成认证和用户授权后,服务B会拿到一个Access Token,只要在后续请求中带上这个Access Token,服务A就会为其提供资源。

2.术语解释
OAuth1.0定义了3种角色:

User:登录的用户,即上述例子中使用两项服务的用户。

Service Provider:服务提供商,即例子中的服务A。

Consumer:服务消费者,如例子中的服务B。

OAuth2.0定义了4种角色:

Resource Owner:资源所有者,即登录用户。相当于1.0中的User

Resource Server:资源服务器,相当于1.0中的Service Provider。

Client:客户端,相当于1.0中的Consumer。

Authorization Server:资源服务器,即服务提供商存放用户生成的资源的服务器。它与认证服务器,可以是同一台服务器,也可以是不同的服务器。相当于1.0中的Service Provider。

OAuth1.0
1.基本步骤
OAuth1.0认证授权就三个步骤,下面以上述照片存储服务为例子说明过程:

步骤

url例子

1. 获取未授权的Request Token

https://photos.example.net/oauth/1.0/initiate

2.获取用户授权的Request Token

https://photos.example.net/oauth/1.0/authorize

3.用授权的Request Token换取Access Token

https://photos.example.net/oauth/1.0/token

当应用拿到Access Token后,就可以有权访问用户授权的资源了。这三个步骤对应OAuth的三个URL服务地址,具体流程如下图所示:

A.Consumer申请Request Token(/oauth/1.0/initiate)

Consumer需要事先去Service Provider注册,获取consumer_key和consumer_secret,该请求的oauth_signature就是由consumer_secret签名而来

POST /oauth/1.0/initiate HTTP/1.1
#参数
oauth_consumer_key=“dpf43f3p2l4k3l03”,
oauth_signature_method=“HMAC-SHA1”,
oauth_timestamp=“137131200”,
oauth_nonce=“wIjqoS”,
oauth_signature=“74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D”

B.Service Provider返回Request Token

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03

C.Consumer重定向User到Service Provider(/oauth/1.0/authorize)

https://photos.example.net/oauth/1.0/authorize?oauth_token=hh5s93j4hdidpola&oauth_callback=http%3A%2F%2Fprinter.example.com%2Fready

D.User登陆授权

E.Service Provider在用户授权后重定向User到Consumer

http://printer.example.com/ready?oauth_token=hh5s93j4hdidpola

F.Consumer申请Access Token(/oauth/1.0/token)

该请求的oauth_signature由consumer_secret+token_secret签名而来

POST /oauth/1.0/token HTTP/1.1
#参数
oauth_consumer_key=“dpf43f3p2l4k3l03”,
oauth_token=“hh5s93j4hdidpola”,
oauth_signature_method=“HMAC-SHA1”,
oauth_timestamp=“137131201”,
oauth_nonce=“walatlh”,
oauth_signature=“gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D”

G.Service Provider返回Access Token:

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00

2.安全保障
为保障安全,OAuth1.0主要做了以下几点:

签名:为保证请求没有被篡改,在步骤A和步骤F中,Consumer都对请求进行了签名,签名的key分别是consumer_secret和consumer_secret+token_secret

oauth_nonce:它用来防止重放攻击,Service Provider应该验证唯一性,不过保存所有的oauth_nonce并不现实,所以一般只保存一段时间(比如最近一小时)内的数据。

oauth_timestamp:如果不验证oauth_timestamp,那么一旦攻击者拦截到某个请求后,只要等到限定时间到了,oauth_nonce再次生效后就可以把请求原样重发,签名自然也能通过,完全是一个合法请求,所以说Service Provider必须验证oauth_timestamp和系统时钟的偏差是否在可接受范围内(比如十分钟),如此才能彻底杜绝重放攻击。

3.漏洞
针对OAuth1.0,攻击者可以通过以下步骤获取Access Token

攻击者用自己的账户访问Consumer,并发起OAuth认证流程

对于C步骤,攻击者不按照Consumer的要求重定向到Service Provider,而是将url中的Request Token保存下来

攻击者利用Request Token,引诱用户点击链接获取授权,即代替真正的Consumer完成C步骤,这里对回调地址的利用有两种方式:

如果Service Provider没有限制回调地址(应用设置没有限定根域名一致),那么攻击者可以把oauth_callback设置成成自己的URL,当User完成授权后,通过这个URL自然就能拿到User的Access Token。

如果Consumer不使用回调地址(桌面或手机程序),而是通过User手动拷贝粘贴Request Token完成授权的话,那么就存在一个竞争关系,只要攻击者在User授权后,抢在User前面发起请求,就能拿到User的Access Token。

OAuth1.0a
1.漏洞修复
针对OAuth1.0的漏洞,OAuth1.0a采取了一些手段来修复。包括以下两点:

Consumer申请Request Token时,必须传递oauth_callback,而申请Access Token时,不需要传递oauth_callback。通过前置oauth_callback的传递时机,让oauth_callback参与签名,从而避免攻击者假冒oauth_callback。

Service Provider获得User授权后重定向User到Consumer时,返回oauth_verifier,它会被用在Consumer申请Access Token的过程中。攻击者无法猜测它的值。

步骤如下:

A.Consumer申请Request Token(/oauth/1.0a/initiate)

POST /oauth/1.0a/initiate HTTP/1.1

#参数
oauth_consumer_key=“dpf43f3p2l4k3l03”,
oauth_signature_method=“HMAC-SHA1”,
oauth_timestamp=“137131200”,
oauth_nonce=“wIjqoS”,
oauth_callback=“http%3A%2F%2Fprinter.example.com%2Fready”, #新增
oauth_signature=“74KNZJeDHnMBp0EMJ9ZHt%2FXKycU%3D”

B.Service Provider返回Request Token

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=hh5s93j4hdidpola&oauth_token_secret=hdhd0244k9j7ao03&oauth_callback_confirmed=true
#oauth_callback_confirmed是为了说明Service Provider是否支持OAuth1.0a版本

C.Consumer重定向User到Service Provider(/oauth/1.0a/authorize)

https://photos.example.net/oauth/1.0a/authorize?oauth_token=hh5s93j4hdidpola
#去掉oauth_callback参数

D.User登陆授权

E.Service Provider在用户授权后重定向User到Consumer

http://printer.example.com/ready?oauth_token=hh5s93j4hdidpola&oauth_verifier=hfdp7dh39dks9884
#多返回一个oauth_verifier
F.Consumer申请Access Token(/oauth/1.0a/token)

POST /oauth/1.0a/token HTTP/1.1

#参数
oauth_consumer_key=“dpf43f3p2l4k3l03”,
oauth_token=“hh5s93j4hdidpola”,
oauth_signature_method=“HMAC-SHA1”,
oauth_timestamp=“137131201”,
oauth_nonce=“walatlh”,
oauth_verifier=“hfdp7dh39dks9884”, #新增
oauth_signature=“gKgrFCywp7rO0OXSjdot%2FIHF7IU%3D”

G.Service Provider返回Access Token:

HTTP/1.1 200 OK
Content-Type: application/x-www-form-urlencoded

oauth_token=nnch734d00sl2jdk&oauth_token_secret=pfkkdhi9sl3r4s00

2.桌面或手机应用
桌面或手机应用通常没有服务端,无法设置Web形式的oauth_callback地址,此时应该把它设置成oob(out-of-band),当用户选择授权后,Service Provider在页面上显示PIN码(也就是oauth_verifier),并引导用户把它粘贴到应用里完成授权。

一个问题是应用如何打开用户授权页面呢?很容易想到的做法是使用内嵌浏览器,但这是个对用户不友好的做法,因为一旦浏览器内嵌到程序里,那么用户输入的用户名密码就有被监听的可能;对用户友好的做法应该是打开新窗口,弹出系统默认的浏览器,让用户在可信赖的上下文环境中完成授权流程。

不过这样的方式需要用户在浏览器和应用间手动切换,才能完成授权流程,某种程度上说,影响了用户体验。好在可以通过一些其它的技巧来规避这个问题,其中一个行之有效的办法是Monitor web-browser title-bar,简单点说,操作系统一般提供相应的API可以让应用监听桌面上所有窗口的标题,应用一旦发现某个窗口标题符合预定义格式,就可以认为它是我们要的PIN码,无需用户参与就可以完成授权流程。

还有一点需要注意的是对桌面或移动应用来说,consumer_key和consumer_secret通常都是直接保存在应用里的,所以对攻击者而言,理论上可以通过反编译之类的手段解出来。进而通过consumer_key和consumer_secret签名一个伪造的请求,并且在请求中把oauth_callback设置成自己控制的URL,来骗取用户授权。为了屏蔽此类问题,Service Provider需要强制开发者必须预定义回调地址:如果预定义的回调地址是URL方式的,则需要验证请求中的回调地址和预定义的回调地址是否主域名一致;如果预定义的回调地址是oob方式的,则禁止请求以URL的方式回调。

3.缺点
虽然OAuth1.0a修复了OAuth1.0的漏洞,但还是存在以下缺点:

签名逻辑过于复杂,对开发者不够友好;

授权流程太过单一,除了Web应用以外,对桌面、移动应用来说不够友好。

OAuth2.0
为了弥补OAurh1.0a的短板,OAuth2.0做了一些改变,总的来说,主要有以下两点:

去掉签名,改用SSL(HTTPS)确保安全性,所有的token不再有对应的secret存在,这也直接导致OAuth2.0不兼容老版本。

针对不同的情况使用不同的授权流程,和老版本只有一种授权流程相比,新版本提供了4种授权模式,可依据客观情况选择。

4种授权模式分别为:

授权码模式(Authorization Code)

简化模式(Implicit Grant)

密码模式(Resource Owner Password Credentials)

客户端模式(Client Credentials)

1.授权码模式 Authorization Code
此类型可用于有服务端的应用,是最贴近老版本的方式。基本流程就是拿Authorization Code换Access Token。该模式下可以有更新令牌(Refresh Token),在更新令牌过期前,可以通过更新令牌刷新Access Token,而不必再走一次授权流程。

A.Client向Authorization Server发出申请(/oauth/2.0/authorize)

GET /oauth/2.0/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

#参数
response_type
client_id
state
redirect_uri

B.用户授权

C.Authorization Server在Resource Owner授权后给Client返回Authorization Code

HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA&state=xyz
D.Client向Authorization Server发出申请(/oauth/2.0/token)

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

#参数
grant_type=authorization_code
code=SplxlOBeZQQYbYS6WxSbIA
redirect_uri
client_id
client_secret
E.Authorization Server在Resource Owner授权后给Client返回Access Token和Refresh Token

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

2.简化模式 Implicit Grant
此类型可用于没有服务端的应用,比如Javascript应用。不通过第三方应用程序的服务器,直接在浏览器中向认证服务器申请令牌,跳过了"授权码"这个步骤,因此得名。所有步骤在浏览器中完成,令牌对访问者是可见的,且客户端不需要认证。

A.客户端将用户导向认证服务器。

B.用户决定是否给于客户端授权。

C.假设用户给予授权,认证服务器将用户导向客户端指定的"重定向URI",并在URI的Hash部分包含了访问令牌。

D.浏览器向资源服务器发出请求,其中不包括上一步收到的Hash值。

E.资源服务器返回一个网页,其中包含的代码可以获取Hash值中的令牌。

F.浏览器执行上一步获得的脚本,提取出令牌。

G.浏览器将令牌发给客户端。

3.密码模式(Resource Owner Password Credentials)
不管有无服务端,此类型都可用。用户向客户端提供自己的用户名和密码。客户端使用这些信息,向"服务商提供商"索要授权。在这种模式中,用户必须把自己的密码给客户端,但是客户端不得储存密码。这通常用在用户对客户端高度信任的情况下。一般不支持Refresh Token。

A.用户向客户端提供用户名和密码。

B.客户端将用户名和密码发给认证服务器,向后者请求令牌。

C.认证服务器确认无误后,向客户端提供访问令牌。

4.客户端模式(Client Credentials)
不管有无服务端,此类型都可用。客户端以自己的名义,而不是以用户的名义,向"服务提供商"进行认证。严格地说,客户端模式并不属于OAuth框架所要解决的问题。在这种模式中,用户直接向客户端注册,客户端以自己的名义要求"服务提供商"提供服务,其实不存在授权问题。

A.客户端向认证服务器进行身份认证,并要求一个访问令牌。

B.认证服务器确认无误后,向客户端提供访问令牌。

5.补充
下面列出一些协议中的实现细节,以及个人早期的一些疑惑和解释。

(1)OAuth2.0中的token_type是什么?

客户端拿到Access Token后像资源服务器访问一些受保护的资源,资源服务器需要验证这个Access Token,如是否过期、是否超出授权范围等。资源服务器如何验证Access Token并非OAuth的讨论范围,但通常涉及资源服务器和认证服务器之间的交互。这意味着,客户端要如何正确地使用Access Token是由认证服务器决定的,而token_type就是认证服务器告知客户端如何使用Access Token的方式。

当token_type为"bearer"时,客户端只需要在请求中带上Access Token即可;当token_type为"mac"时,客户端就需要利用Access Token计算一个签名带在请求中。使用协议的时候也可以自行扩展一些token_type。

(2)授权码模式中为什么不直接返回token而是要先返回code,再用code换token?

直接返回不安全。因为认证服务器认证通过后是需要浏览器302重定向到你的服务器,若你的服务器不支持https,直接返回的token可能被别人获取到。

那么为什么需要浏览器重定向到你的服务器,而不直接让认证服务器直接调用你的接口?问题在于如果认证服务器直接调用你的接口,那你应该怎样让浏览的页面进行跳转呢?这时候发送请求的不是浏览器了,而是认证服务器,所以你只能给认证服务器发送一个url地址,让认证服务器控制浏览器进行跳转。这样肯定会加大认证服务器的压力。

那多一步获取 code 有什么用呢?如果你的服务器不支持 https,code不也是会被暴露吗?攻击者拿到code,直接用获取到的code,访问认证服务器获取Access Token。针对这个问题,OAuth2.0协议对此的处理方式为:

code只能被使用一次

如果code被使用来两次,之前通过code获取的Access Token会失效。因此,若是攻击者比正常用户先用了 code 也没事

(3)client_secret有什么用?

所有客户端在访问认证服务器之前都需要先注册,获取一个client_id和一个client_secret,client_id用来让客户端表示“我是谁”,client_secret用来让客户端表示“我确实是我”。假设没有client_secret,若客户端的DNS被污染,code被直接返回给攻击者,此时code不会被使用两次,攻击者就拿到了Access Token。但若是有client_secret,由于攻击者不知道client_secret,认证服务器发现client_secret不正确后,不会将Access Token返回。

(4)state参数有什么用?

state参数用来防止跨站请求伪造(CSRF)。首先来看看CSRF是什么,CSRF利用的是网站对网页浏览器的信任。以一个例子来说明:

转存失败
重新上传
取消

可以看出,用户在登出可信网站前,如果在攻击者的引导下,点击了一些链接从而对可信网站发出了请求,浏览器会默认带上用户在可信网站的信息。可信网站则会认为这个请求是用户自愿发起的,而用户对这个请求毫不知情。CSRF攻击是源于WEB的隐式身份验证机制,WEB的身份验证机制虽然可以保证一个请求是来自于某个用户的浏览器,但却无法保证该请求是用户批准发送的。对此,有一些常见的防御措施:

检查Referer字段

HTTP头中有一个Referer字段,这个字段用以标明请求来源于哪个地址。在处理敏感数据请求时,通常来说,Referer字段应和请求的地址位于同一域名下。

这种办法简单易行,工作量低,局限性在于其完全依赖浏览器发送正确的Referer字段。虽然http协议对此字段的内容有明确的规定,但并无法保证来访的浏览器的具体实现,亦无法保证浏览器没有安全漏洞影响到此字段。并且也存在攻击者攻击某些浏览器,篡改其Referer字段的可能。

添加校验token

由于CSRF的本质在于攻击者欺骗用户去访问自己设置的地址,所以如果要求在访问敏感数据请求时,要求用户浏览器提供不保存在cookie中,并且攻击者无法伪造的数据作为校验,那么攻击者就无法再运行CSRF攻击。这种数据通常是窗体中的一个数据项。服务器将其生成并附加在窗体中,其内容是一个伪随机数。当客户端通过窗体提交请求时,这个伪随机数也一并提交上去以供校验。正常的访问时,客户端浏览器能够正确得到并传回这个伪随机数,而通过CSRF传来的欺骗性攻击中,攻击者无从事先得知这个伪随机数的值,服务端就会因为校验token的值为空或者错误,拒绝这个可疑请求。

再来看看OAuth中的CSRF是怎么样。

转存失败
重新上传
取消

从上图可以看出,受害者以为自己绑定了自己在认证服务器上的账号,而实际上他绑定的是攻击者的账号。这时候他在客户端上的操作,可以被攻击者获取到。那么state参数如何防止CSRF?首先,客户端开发者会为受害者和攻击者各自生成一个state,第1步认证服务器返回回调url中会带上攻击者的state。当受害者开始第5步时,带上的state也是攻击者的,客户端检查state,发现不是受害者的state,就不会再去向认证服务器申请token,而是直接返回错误。

微信三方授权方案
微信提供了三方授权机制,在得到公众号或小程序运营者授权后,第三方平台开发者可以通过调用微信开放平台的接口能力,为公众号或小程序的运营者提供账号申请、小程序创建、技术开发、行业方案、活动营销、插件能力等全方位服务。微信的三方授权机制基本遵循OAuth2.0,与授权码模式较为相似。

1.获取token步骤
步骤

说明

1、配置授权事件URL,用于接收component_verify_ticket

第三方平台创建审核通过后,微信服务器每隔10分钟会向第三方的消息接收地址推送一次component_verify_ticket,用于获取第三方平台接口调用凭据。omponent_verify_ticket有效期为12h

2、获得component_verify_ticket后,调用接口获取component_access_token

component_access_token有效期2h,当遭遇异常没有及时收到component_verify_ticket时,建议以上一次可用的component_verify_ticket继续生成component_access_token

3、获得component_access_token后,调接口获取pre_auth_code

pre_auth_code用于生成授权二维码或链接

4、引导用户扫描二维码或点击链接来完成授权,获取授权后的authorization_code

authorization_code有过期时间,该过期时间在授权后回调 URI进行返回;也会通过推送授权变更通知

的方式将authorization_code推送给第三方平台

5、获得authorization_code后,调接口获取authorizer_access_token和authorizer_refresh_token

通过授权码和自己的接口调用凭据(component_access_token),换取公众号或小程序的接口调用凭据(authorizer_access_token和用于前者快过期时用来刷新它的 authorizer_refresh_token)和授权信息(授权了哪些权限等信息)

6、利用authorizer_refresh_token,调接口刷新authorizer_access_token

可通过authorizer_refresh_token来调用公众号或小程序的接口

7、按照接口文档,代替公众号或小程序调用接口

在完成授权后,第三方平台可通过公众号或小程序的接口调用凭据(authorizer_access_token)来代替它调用接口

注:具体接口文档可见验证票据 | 微信开放文档

2.与OAuth2.0的区别
相比于标准的OAuth2.0的授权码模式,微信的授权机制主要有以下几点不同:

注册的第三方平台不能直接调用认证服务器的接口,而是在注册时配置一个url来接收component_verify_ticket,再用component_verify_ticket换取component_access_token,通过component_access_token才有用访问认证服务器的权限。

认证服务器不直接返回code,而是返回一个pre_auth_code,第三方平台利用pre_auth_code生成一个二维码或链接,用户通过扫描二维码或点击链接给第三方平台授权(即给一个authorization_code)。

3.消息加解密
开放平台的消息加密解密技术方案基于AES加解密算法来实现,算法中需要用到对称密钥,密钥信息如下:

EncodingAESKey: 即消息加解密Key,长度固定为43个字符,由字母和数字组成。由开发者在创建公众号插件时填写,后也可申请修改。

AESKey: AESKey=Base64_Decode(EncodingAESKey + “=”),EncodingAESKey尾部填充一个字符的 “=”, 用Base64_Decode生成 32 个字节的 AESKey。

加密算法

msg_encrypt = Base64_Encode(AES_Encrypt[random(16B) + msg_len(4B) + msg + AESKey])。

%加密的buf由16个字节的随机字符串、4个字节的msg_len(网络字节序)、msg和32个字节的AESKey

签名算法

msg_signature = sha1(sort(Token、timestamp、nonce, msg_encrypt))

%Token 微信开放平台上,服务方设置的接收消息的校验token
%timestamp 时间戳
%nonce 随机数
%msg_encrypt 前文描述密文消息体

解密算法

1. aes_msg=Base64_Decode(msg_encrypt)
2. rand_msg=AES_Decrypt(aes_msg)
3. 验证尾部
4. 去掉rand_msg头部的16个随机字节,4个字节的msg_len和尾部

(1)接收消息
第三方平台可能会接收到两种类型的消息:

用户发送给公众号/小程序的消息(由第三方平台代收)。此时,消息XML体中,ToUserName(即接收者)为公众号/小程序原始ID(可通过获取授权方的帐号基本信息接口来获得)。

微信服务器发送给第三方平台自身的通知或事件推送(如取消授权通知、component_verify_ticket 推送等)。此时,消息XML体中没有ToUserName字段,而是AppId字段,即第三方平台的AppId。这种系统事件推送通知,服务开发者收到后也需进行解密,接收到后只需直接返回字符串“sucess”。

下面以例子说明:

A.微信平台发送信息

现有明文信息如下:

        1348831860         1234567890123456

加密后得到密文msg_encrypt,给第三方平台发送的消息包括url中的timestamp、nonce、encrypt_type、msg_signature及消息体中的消息(内容如下):

     msg_encrypt

B.第三方平台验证签名

第三方平台收到消息后首先验证签名,用url中的参数和消息体中的消息计算一个签名,若计算出的签名和url中传递过来的msg_signature一致,则验签通过。

C.第三方平台解密消息

按照解密算法解密消息体中的消息,获取原始的消息明文。

(2)发送消息
第三方平台发送给微信平台的消息也需要加密,假设有明文消息如下:

        12345678      

加密后得到密文msg_encrypt,最后发送的消息如下:

  msg_encrypt         

阿里郎目前三方授权方案
要拿到Access Token,主要可以分为获取用户授权、申请访问令牌2个步骤。阿里郎目前三方授权方案类似于结合了授权码模式和客户端模式,对外接口基本符合OAuth2.0协议。

1.授权相关的操作,如用户授权、取消授权等功能,需要阿里郎客户端的配合,客户端将请求发送至阿里郎资源服务器,再由阿里郎资源服务器与阿里郎认证服务器之间通过hsf调用完成交互。

2.令牌相关的操作,如申请令牌、刷新令牌等功能,可由第三方应用直接与认证服务器通过http请求交互。对于http请求做如下约定:

调用接口时需要在参数中上传时间戳

调用接口时需要使用分发的client_secret对参数进行签名

1.应用注册
在授权和申请令牌的过程中,要对第三方应用进行认证,即只有经过注册的第三方应用(如钉钉),才能调用认证服务器的接口。对此,阿里郎认证服务器的实现方式是:

在认证服务器中配置第三方应用的信息,并将信息中的client_id、client_secret预埋到第三方应用中。

client_id可以唯一标识一个第三方应用,所有请求都需要带上该参数,用以检测请求发送方是否有权限

http请求需要使用client_secret对参数进行签名(加签过程只涉及url中的参数,对于body中的参数暂不处理),用以验证消息的来源,具体步骤如下:

生成当前时间戳(参数名为ts)

将所有参数(包括时间戳),按照参数名排序

将排序后的参数拼接后,末尾拼接client_secret

对拼接结果进行md5加密,加密后的结果随其他参数一起发送(参数名为signature)

2.用户授权
不同于OAuth2.0中的客户端模式,阿里郎认证服务器并非直接无条件信任已经注册的第三方应用,而是需要经过用户的授权,一方面因此需要在第三方应用和认证服务器之间传递用户信息。如在申请访问令牌时,第三方应用需要向认证服务器表明:我是谁(client_id)、我确实是我(client_secret加签)、我是在谁的授权下申请(用户信息)。用户信息通过user_identifier参数传递,认证服务器支持用户令牌和员工号两种方式,并通过user_identifier_type参数指定类型(枚举类型为USER_TOKEN、EMPLOYEE_NO)。

3.获取token步骤
步骤

说明

1.为第三方应用配置client_id和client_secret,并预埋到第三方应用的服务器中

只有配置过client_id和client_secret的第三方应用,才有接下来获取相关授权的资格,client_secret还可用于在http请求中对请求来源进行签名认证

2.第三方应用申请user_token

向认证服务器申请,user_token标识了后续的授权的用户信息

3.利用client_id和user_token获取用户授权

该操作需要唤起阿里郎客户端,阿里郎客户端和阿里郎资源服务器交互,阿里郎资源服务器再通过hsf调用认证服务器的授权服务,完成用户授权

4.申请访问令牌access_token和刷新令牌refresh_token

第三方应用直接向认证服务器申请。有了access_token后,第三方应用就可以操作其他资源(如vpn),操作需要经过阿里郎客户端的辅助

5.access_token快过期时,可以利用refresh_token进行刷新

第三方应用直接向认证服务器申请刷新

注:具体接口文档还在完善中

总结
OAuth协议为用户资源的授权提供了一个安全的、开放而又简易的标准,发展至OAuth2.0,在安全性和易用性上都有了很大的提升。有许多应用在OAuth的基础上,设计了自身的授权机制,文中列出的微信和阿里郎的方案就是其中两个。可以看出不同应用可以针对自身的需求对OAuth做一些定制,如微信通过component_verify_ticket控制谁能访问认证服务器,而阿里郎则通过实现配置的client_id来控制。

 类似资料: