大话Oauth2.0(一)从概念到实践

Oauth2.0本身:

Oauth2.0是一种授权协议,当然也归属为安全协议的范畴,在实际执行的时候就是保护互联网中不断增长的大量WEB API的安全访问。OAuth2.0共包含四种角色,分别是资源所有者、第三方应用(也称为客户端client)、授权服务器和资源服务器。如下图所示,某公司A开发了一个微信小程序(第三方应用)可以帮助我(资源所有者)美化微信服务器(资源服务器)上面的头像,我在用这个微信小程序开发的美化头像功能的时候,首先要给微信小程序授权(授权服务器),这个微信小程序才能访问我的头像,实际上访问的时候微信小程序就是通过WEB API来调用的。授权的过程中我是不可能把我的账号密码给它的,这样的前提下就会有另外方式的授权,也就是上面介绍的现在国际通用的标准OAuth2.0。

image

OAuth2.0协议流程描述了四种角色之间的交互过程,如下图所示。

image

上面的序列图一共分为以下6个步骤:

(1)第三方应用请求资源所有者授权。

(2)资源所有者同意给第三方应用授权。

(3)第三方应用使用步骤2中获得的授权,向授权服务器申请令牌。

(4)授权服务器对第三方应用进行认证并确认无误后,同意发放令牌。

(5)第三方应用使用步骤4中发放的令牌向资源服务器申请获取资源。

(6)资源服务器确认令牌无误后,向第三方应用开放资源访问。

关于更详细的Oauth2.0流程介绍,也可以参考《架构修炼之道》第2章 "开放之道" 内容。

第三方开发者ISV的实践:

如果按照开发语言类型来区分可以有JAVA、.net、PHP等不同的ISV应用类型,但语言分类对于授权的场景意义不大,因为虽然是不同的语言但大家都是遵守Oauth2.0协议。在授权上区分ISV接入流程我们则需要按照ISV应用有无Web Server来区分。有Web Server的归为一类,走Server-side flow;没有Web Serve的归为另外一类,走Client-side flow流程。

本文先介绍Server-side flow流程,共包含3个步骤。

步骤一:****拼接授权URL

以京东宙斯开放平台为例

https://open-oauth.jd.com/oauth2/to_login?app_key=XXXXX&response_type=code&redirect_uri=XXXXX

有三个必填参数,分别是app_key,response_type,redirect_uri,下面是该三个参数的具体含义。

名称 是否必填 参数值
app_key ISV在宙斯开放平台创建应用的时候由系统分配
response_type 固定值code,默认采用code换取token的方式
redirect_uri ISV的应用访问地址,创建应用的时候由ISV配置

PS:左右滑动查看完整列表

注:如果是通过开放平台直接使用,ISV可以不用拼接该授权URL,会由开放平台一方的系统自动读取ISV的应用地址然后拼接好返回给客户端,后文在平台一方的实践介绍中有叙述。

步骤二:****引导用户登录授权

引导用户登录,并为ISV应用进行授权。

步骤三:****获取CODE

在用户登录并点击授权之后宙斯会将授权码CODE返回到回调地址上,比如https://redirect_uri?code=CODE&...

ISV获取到CODE之后就可以执行步骤四。

步骤四:****用CODE换取ACCESSTOKEN

https://open-oauth.jd.com/oauth2/access_token?app_key=XXXXX&app_secret=XXXXX&grant_type=authorization_code&code=XXXXX,这里的code是通过步骤三获取到的。

名称 是否必填 参数值
app_key ISV在宙斯开放平台创建应用的时候由系统分配
app_secret 宙斯开放平台分配给当前app_key对应的应用的秘钥
grant_type 固定为authorization_code
code 步骤三中获取到的值

返回值示例:

{

access_token: "a3207b6b5ad04249ad1dbf6a98248bea",//所需要的access_token

expires_in: 3600000,//单位ms,该access_token的过期时间

refresh_token: "4ecbbab0e9e443159c518da1d10741ad"//再不需要用户重新授权的情况下,用于获取新的access_token

}

access_token是有有效期的,这一点是从安全角度来考虑的,因为如果access_token没有时效性,一旦泄露则会被攻击者长期使用。好比我们的个人密码需要定期修改一样。比如笔者跟公司签订了五年期的合同,在这个五年时间内公司要求我们的内部ERP系统的密码需要三个月更新一次,这样避免了密码一旦泄露造成了长期的安全风险。在订购类型的第三方IT工具类应用中access_token是用户授权之后ISV的应用才能获取到的,比如用户购买了该ISV的一款SASS软件为期6个月。如果这6个月时间内出于安全考虑需要更新access_token的值,肯定不能让用户再授权一次,这样体验非常不友好。此时refresh_token就派上了用场,ISV可以利用refresh_token去请求开放平台获取新的access_token的值。

当用户授权ISV之后会获取到一个accestoken,对于ISV来讲accestoken相当于登录成功了ISV软件之后的PIN。此时ISV需要将这个用户的accestoken会话保存下来,可以加密存储到cookie中,也可以放到ISV软件服务端的session中。ISV软件后续访问平台的接口要求必须都要有该accestoken,这样会话状态被保存下来之后则会使得ISV应用调用平台接口更加的便利,实际上相当于用户登录了一个普通的WEB应用之后,每次这个WEB应用服务端程序都能获取到该用户的会话信息一样。

平台一方的实践:

我作为一个用户需要授权给我当前将要使用的软件服务商,这样软件服务商才可以被允许去访问我的数据。理论上我是需要每次都进行授权动作才能完成软件的使用,那么我们平时会发现比如在微信中使用第三方小程序的时候我只在第一次访问的时候进行了授权动作,以后使用的时候并没有要求我再次进行授权,这是如何实现的呢。再比如下图所示,在用户点击立即使用的时候也会遇到同样的场景:初次使用和再次使用。

image

这就需要作为平台一方来实现控制逻辑,同时这个也建立在用户在平台使用的基础之上。平台一方在第一次用户授权之后会按照Oauth2.0的规范生成一个accesstoken,随后将该accesstoken与当前用户的pin进行绑定存储到数据库中,如下图所示。当然该授权关系是有时效的(这个时效需要依据平台的实际环境来确定)。当用户再次来通过平台去访问该软件的时候,平台会根据当前登录的用户pin去判断先前已经保存的绑定关系,如果有绑定关系则允许用户直接使用软件,在返回的startUrl中是一个带有code的ISV软件的启动地址,如果没有绑定关系则会直接利用第三方开发者已经接入的规范的Oauth2.0协议流程,返回的startUrl中是一个授权的页面地址,由第三方开发者去引导用户进行授权。

image

总结

本文先后从概念到开发者实践以及平台一方的实践去介绍了Oauth2.0。它是一个基于HTTP的协议,如果采用授权码类型比如本文中所述的Server-side flow则要求必须有Web Server,我们也正是按照该流程去介绍如何实践的。

想了解Oauth2.0的同学请持续关注,未完待续... 文章第一时间发布在公众号

程序架道

推荐阅读更多精彩内容