OAuth2.0原理与实现

弄懂了原理流程,才可以搭建出来。更重要的是,可以根据原理流程自定义搭建,甚至可以完全自己实现一套,最后运行效果和原理和这个对得上就成功了,不要总期待标准答案!

首先参考两篇博客:

阮一峰的博客以及张开涛的博客

图分析补充:

第一步访问授权页面(然后授权服务器返回授权页面)时,即需要预先申请的开发者信息如client_id、redirect_uri,而授权服务器在第三步用户手动授权后才用到这些信息,进行页面跳转(response状态码302,附上redirect地址和授权码等),第四步是第三部redirect_uri所指向的应用开发后台地址,是第三步的网页自动重定向所访问到的,这个后台逻辑要自己写,内容是拼接redirect_uri和授权码code参数,访问认证服务器后台,申请令牌返回。这样应用获取到令牌,此后使用令牌继续访问资源服务器获取资源进行使用,由此跳转到自己的应用页面(一般是一个绑定手机号页面,使用微信账号与之绑定,是一个注册,把微信账号access_token与手机号账号绑定,存入应用自身数据库,当然access_token存在有效期,过期后也需要在用户再次登录时在自身数据库透明更新)

(A)用户访问客户端,后者将前者导向认证服务器。

(B)用户选择是否给予客户端授权。

(C)假设用户给予授权,认证服务器将用户导向客户端事先指定的"重定向URI"(redirection URI),同时附上一个授权码。

(D)客户端收到授权码,附上早先的"重定向URI",向认证服务器申请令牌。这一步是在客户端的后台的服务器上完成的,对用户不可见。

(E)认证服务器核对了授权码和重定向URI,确认无误后,向客户端发送访问令牌(access token)和更新令牌(refresh token)。

下面是上面这些步骤所需要的参数。

A步骤中,客户端申请认证的URI,包含以下参数:

  • response_type:表示授权类型,必选项,此处的值固定为"code"
  • 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

C步骤中,服务器回应客户端的URI,包含以下参数:

  • code:表示授权码,必选项。该码的有效期应该很短,通常设为10分钟,客户端只能使用该码一次,否则会被授权服务器拒绝。该码与客户端ID和重定向URI,是一一对应关系。
  • state:如果客户端的请求中包含这个参数,认证服务器的回应也必须一模一样包含这个参数。

下面是一个例子。


HTTP/1.1 302 Found
Location: https://client.example.com/cb?code=SplxlOBeZQQYbYS6WxSbIA
          &state=xyz

D步骤中,客户端向认证服务器申请令牌的HTTP请求,包含以下参数:

  • grant_type:表示使用的授权模式,必选项,此处的值固定为"authorization_code"。
  • code:表示上一步获得的授权码,必选项。
  • redirect_uri:表示重定向URI,必选项,且必须与A步骤中的该参数值保持一致。
  • client_id:表示客户端ID,必选项。

下面是一个例子。


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

grant_type=authorization_code&code=SplxlOBeZQQYbYS6WxSbIA
&redirect_uri=https%3A%2F%2Fclient%2Eexample%2Ecom%2Fcb

E步骤中,认证服务器发送的HTTP回复,包含以下参数:

  • access_token:表示访问令牌,必选项。
  • token_type:表示令牌类型,该值大小写不敏感,必选项,可以是bearer类型或mac类型。
  • expires_in:表示过期时间,单位为秒。如果省略该参数,必须其他方式设置过期时间。
  • refresh_token:表示更新令牌,用来获取下一次的访问令牌,可选项。
  • scope:表示权限范围,如果与客户端申请的范围一致,此项可省略。

下面是一个例子。


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

从上面代码可以看到,相关参数使用JSON格式发送(Content-Type: application/json)。此外,HTTP头信息中明确指定不得缓存。

 

具体到应用开发上,无论是网站还是手机App,原理都是相同的:用户访问应用(比如微信登录功能),应用(使用申请的开发者信息如client_id、redirect_uri)访问资源服务器(微信后台,一个例子是很多App直接请求打开微信客户端,那么必然是访问微信后台),返回授权页面(微信授权)。用户输入资源服务器用户名/密码(微信),或直接获取已登录账号(比如手机App微信账号),点击授权(认证服务器验证微信账号信息)。授权后根据该应用此前传递的redirect_uri重定向到应用页面并附上授权码(code),应用使用重定向的网址和授权码访问应用自己的后台,由后台再不可见地使用redirect_uri、code到认证服务器获取令牌(access_token,可选refresh_token)返回。

 

一个细节是返回授权页面,传统方式是应用内嵌一个微信用户名/密码登录页面(由微信后台返回),现在一般使用二维码扫描方式。

一个例子是微信网页版的微信授权登录:

根据https://www.oschina.net/question/1172551_218058和亲自追踪,采用的是二维码ajax轮询方式:

“打开https://wx.qq.com/用FireFox看吧,它不断轮询https://login.weixin.qq.com/cgi-bin/mmwebwx-bin/login?uuid=xxxx&tip=1&_=1419316260694

用微信一扫这个结果就变了。”

“ajax轮训,前台每隔5秒访问一次后台,如果返回1这跳转,如果不是则继续查询

手机扫描二维码实际上是访问一个链接,当成功扫描,则把上一个链接返回1.这样电脑端就可以跳转了”

这个网页(打开后)上的二维码实际是不断轮询ajax访问微信后台的:

微信扫码后,微信后台一个字段改变(如此感知扫码),应用会访问到该微信用户信息(比如图片),等待授权,地址不跳转(不变),同时会继续轮询此前轮询的微信后台地址:

而扫码后的手机端,最终访问的是微信后台返回的授权页面(实际带上了应用的client_id、redirect_uri信息,经过了应用后台的转发)。手机端点击授权之后,微信后台有另一个特定标示字段的改变(数据库或缓存),这样网页二维码页面的ajax长轮询(无论的Comet技术还是WebSocket,均可立即推送这个字段值改变)就可访问到这个改变的字段,根据微信后台授权逻辑,就可立即跳转到授权后的页面(redirect_uri):

这就是网页自动跳转的原理。其实后台透明地经历了另一个获取令牌的过程,让网页直接根据令牌访问到了该微信用户(资源服务器)的有限制资源(这里特殊,几乎是该用户的所有资源)。

 

App应用(区别于网站应用)的授权过程表现出请求手机系统(表现为出现弹出框让用户同意)访问微信客户端,使用微信客户端(内嵌在App应用中,所以此时实际仍然在该App应用中进行操作,所以才有后来的跳转回App应用申请完授权资源后的页面)访问微信后台,返回一个授权页面(应用内访问微信,实际仍然在应用页面里)。用户点击授权后,自动跳转到应用页面(现在很多应用都有一个手机号绑定,短信验证码验证后进行绑定的页面,点击后进入真正的应用页面,一般是个人主页)。其实后台经历了一个获取授权码(authorization code)、重定向、获取令牌(access token),再用令牌有限制访问资源(仍然指微信账号信息)的过程。

 

令牌(access token)一般有很短的有效期(2小时),需要使用refresh_token来定期刷新,refresh_token有效期较长(30天),失效后需要用户重新授权。表现为退出登录后再微信登录,短期内不再需要用户再手动进行微信授权,说明短期内(access token有效)仍然有权访问该有限制资源(微信账号信息)。

后台需要有用户名/密码认证、授权机制,保存令牌的数据库或缓存,令牌生成接口,刷新令牌接口,过期自动删除令牌机制(参考Spring SSO的JWT令牌配置和实现),资源服务权限配置,验证令牌放行或拒绝资源访问机制。搞清这套逻辑和技术实现原理,尝试自定义实现或完全自主实现。

标准OAuth2.0系统参考:

微信公众开发平台:https://open.weixin.qq.com/cgi-bin/showdocument?action=dir_list&t=resource/res_list&verify=1&id=open1419316505&token=&lang=zh_CN

 

posted @ 2018-09-08 14:55  free_wings  阅读(5607)  评论(0编辑  收藏  举报