简介

说明

        本文介绍OAuth2的使用场景、流程、原理。

官网

​draft-ietf-oauth-v2-31​

什么时候会用到OAuth2?

        OAuth2用于第三方授权。比如,用户在使用A网站,想获得B网站的数据,这时就需要B网站提供oauth2的授权功能让A网站获取。

        例1:有某个流程图网站,支持将流程图存入百度云盘或者从百度云盘读出。

        例2:在登录百度时,可以选择第三方账号登录(微信、钉钉等)。如下图所示:

OAuth2--流程/原理_微信

如果使用QQ:可以扫描二维码登录,也可以选择电脑QQ已经登录的账号:

OAuth2--流程/原理_流程图_02

如果使用微信:则会展示二维码,让你微信扫描二维码登录:

OAuth2--流程/原理_oauth2_03

这个登录二维码是一次性的,下次点击进来会生成新的。

第三方认证为什么要用OAuth2

        假设有某个流程图网站,支持将流程图存入百度云盘或者从百度云盘读出。最简单的办法就是百度云盘直接将用户的账号密码给流程图网站,但这样有如下问题:

  1. “流程图网站”为了后续的服务,会保存用户的密码,这样很不安全。
  2. “流程图网站”拥有了获取用户储存在百度云盘所有资料的权力,用户没法限制”流程图网站”获得授权的范围和有效期。
  3. 用户只有修改密码,才能收回赋予”流程图网站”的权力。
  4. 只要有一个第三方应用程序被破解,就会导致用户密码泄漏,以及所有被密码保护的数据泄漏。

        使用OAuth2可以很完美的解决这些问题,后边会介绍详细流程。

OAuth2介绍

        OAuth2.0是一种允许第三方应用程序使用资源所有者的凭据获得对资源有限访问权限的一种授权协议。

        例如通过微信登录百度,相当于微信允许百度作为第三方应用程序在经过微信用户授权后,通过微信颁发的授权凭证有限地访问用户的微信头像、手机号,性别等资源,从而来构建自身的登录逻辑。

        在OAuth2.0协议中第三方应用程序获取的凭证并不是资源拥有者的用户名和密码。OAuth2允许用户提供一个令牌(token)给第三方网站,一个令牌对应一个特定的第三方网站,且该令牌只能在特定的时间内访问特定的资源。

OAuth1.0与OAuth2.0

OAuth1.0的问题

  1. 开发的流程复杂。因为需要反复的签名。
  2. 授权流程过于单一化。
  3. 没有引入回跳地址的判断,导致回跳可能会篡改,这样授权码和Token会被Hack掉(1.0a的时候修复了这个漏洞)。

认证流程

OAuth2.0的角色

OAuth2.0协议中定义了以下四个角色:

  1. resource owner(资源拥有者)
  1. 拥有某个资源的
  2. 例如我们使用通过微信账号登录百度,而微信账号信息的资源拥有者就是微信用户。
  1. client(客户端)
  1. 用户直接访问到的程序。
  2. 例如我们使用通过微信账号登录百度,客户端就是百度。
  1. authorization server(授权服务器)
  1. 资源服务器这家公司提供,专门用来处理认证授权
  2. 例如我们使用通过微信账号登录百度,授权服务器就是微信开放平台提供的用于认证的服务器,用于给resource server管理第三方授权。
  1. resource server(资源服务器)
  1. 存放资源的服务器。
  2. 接收使用访问令牌对受保护资源的请求并响应,它与授权服务器可以是同一服务器,也可以是不同服务器。
  3. 例如我们使用通过微信账号登录百度,资源服务器就是微信服务器。

总结

        以用户通过微信来登录百度为例,用户这个人是resource owner,拥有微信账号;百度平台是client,是用户想访问的平台;微信账号是resource server,存放着用户的微信数据;微信开放平台是authorization server,用于给resource server管理第三方授权。可以发现,authorization server和resource server一般是同一家公司。

流程图:OAuth2官网

网址:​​draft-ietf-oauth-v2-31​

OAuth2--流程/原理_流程图_04

授权模式及其流程

授权模式是指client获取access_token的方式,官方给了4种,

  1. authorization code。授权码模式
  2. implicit。隐藏模式。
  3. resource owner password。用户的账号密码方式
  4. client。client的id授权方式

当然,也可以自由扩展,能认证返回access_token就行。

授权码模式

简介

        功能最完整、流程最严密的授权模式。

        首先用户让微信给一个code给client,client拿着code找微信换一个access_token,以后就用access_token获取用户的唯一id。

        石墨文档(client)需要在微信开放平台(authorization)注册,获取appId和appSecret。这组凭证是石墨自己访问微信开放平台的。access_token则是代表用户本人,注意区别。

流程

OAuth2--流程/原理_流程图_05

第1步:用户通过浏览器访问石墨文档,然后点击微信登录按钮

        浏览器请求石墨微信登录回调地址,石墨后台返回302,response有location头, 浏览器重定向跳转到微信二维码认证页面。

OAuth2--流程/原理_服务器_06

location的值为:

https://open.weixin.qq.com/connect/qrconnect?appid=wx5a67899f4af8b0b1&redirect_uri=https%3A%2F%2Fshimowendang.com%2Flizard-api%2Fauth%2Fwechat%2Flogin_callback&response_type=code&scope=snsapi_login&state=ee257de2-91af-411e-9f2a-ef2b802bebfb#wechat_redirect

参数

  • appid: 石墨文档作为client在微信开发平台的凭证id
  • redirect_uri: 认证通过后,微信开放平台添加一个code参数到这个url后面,然后浏览器重定向到这个url。这个url是石墨文档后台接收code的接口。
  • response_type: code。固定值。
  • scope: 授权范围,想获取用户哪些资料的api。网页登录为snsapi_login
  • state: 防刷的,防止csrf跨站请求伪造攻击。微信认证成功后,回调的时候会原样返回给石墨(client)。如果没这个,client接收参数就只有code,别人就是随意伪造碰撞code。而石墨生成了一次性token作为state,回调接口只有一次有效期。这里石墨用的uuid.

第2步:用户扫描微信二维码,允许通过微信登录

        用户微信点击了确认,就代表了授权通过了,允许石墨获取access_token。浏览器微信二维码页面收到code,将code作为参数拼接redirect的url,浏览器重定向到石墨后台

OAuth2--流程/原理_服务器_07

请求的url为:

https://shimowendang.com/lizard-api/auth/wechat/login_callback?code=0412xLkl2HION745rIml2UNUng12xLkQ&state=ee257de2-91af-411e-9f2a-ef2b802bebfb

参数

  • code:微信开放平台(authorization server)颁发给石墨文档(client)的code。
  • state:石墨平台用于检测是否有效。

        石墨文档后台会校验state是否有效,然后拿code和appId、appSecret去访问微信接口获取用户信息。

  1. 这一步是在石墨文档后台进行的。浏览器看不到access_token。
  2. 对于上一步生成的code,即便有人拿到code,它已经失效了。
  3. code和state都是一次性的有效期。这样保证了access_token的安全性。

石墨文档后台访问的微信接口:

https://api.weixin.qq.com/sns/oauth2/access_token?appid=APPID&secret=SECRET&code=CODE&grant_type=authorization_code

返回值:

{ 
"access_token":"ACCESS_TOKEN",
"expires_in":7200,
"refresh_token":"REFRESH_TOKEN",
"openid":"OPENID",
"scope":"SCOPE",
"unionid": "o6_bmasdasdsad6_2sgVt7hMZOPfL"
}

第3步:石墨文档登录

 石墨文档获取到用户唯一id(unionid字段)后,根据后台用户账号的绑定关系,确认当前登录用户登录。

隐藏模式

简介

        针对纯web前端应用,没有后台,令牌只能放在前端。这种也叫client side,又称为User Agent Flow。access_token会进行网络传输,并不安全,很少使用这种方案。

        client直接请求authorization server获取access_token,请求参数是client id和redirect url, 最后认证返回后拼接#access_code。

流程

OAuth2--流程/原理_微信_08

QQ互联提供了这样的接口:​​使用Implicit_Grant方式获取Access_Token — QQ互联WIKI​

第1步:获得access_token

请求地址

PC网站: https://graph.qq.com/oauth2.0/authorize

请求方法

GET

请求参数

请求参数请包含如下内容:

参数

是否必须

含义

response_type

必须

授权类型,此值固定为“token”。

client_id

必须

申请QQ登录成功后,分配给应用的appid。

redirect_uri

必须

成功授权后的回调地址,必须是注册appid时填写的主域名下的地址,建议设置为网站首页或网站的用户中心。注意需要将url进行URLEncode。

scope

可选

请求用户授权时向用户显示的可进行授权的列表。

可填写的值是​​API列表​​中列出的接口,以及一些动作型的授权(目前仅有:do_like),如果要填写多个接口名称,请用逗号隔开。

例如:scope=get_user_info,list_album,upload_pic,do_like

不传则默认请求对接口get_user_info进行授权。

建议控制授权项的数量,只传入必要的接口名称,因为授权项越多,用户越可能拒绝进行任何授权。

state

可选

client端的状态值。用于第三方应用防止CSRF攻击,成功授权后回调时会原样带回。

display

可选

PC网站接入时使用。

用于展示的样式。不传则默认展示为PC下的样式。

如果传入“mobile”,则展示为mobile端下的样式。

第2步:回调 

1.如果用户成功登录并授权,则会跳转到指定的回调地址,并在URL后加“#”号,带上Access Token以及expires_in等参数。如果请求参数中传入了state,这里会带上原始的state值。如果redirect_uri地址后已经有“#”号,则加“&”号,带上相应的返回参数。如:

PC网站:http://graph.qq.com/demo/index.jsp?#access_token=FE04************************CCE2&expires_in=7776000&state=test

说明:expires_in是该access token的有效期,单位为秒。

Tips:

  1. 可通过js方法:window.location.hash来获取URL中#后的参数值。
  2. 建议用js设置cookie存储token。

2. 如果用户在登录授权过程中取消登录流程,对于PC网站,登录页面直接关闭;

错误码说明

接口调用有错误时,会返回code和msg字段,以url参数对的形式返回,value部分会进行url编码(UTF-8)。

PC网站接入时,错误码详细信息请参见:​​100000-100031:PC网站接入时的公共返回码​​。

账号密码模式

需要用户把微信账号和密码给石墨文档,石墨拿着去微信换token。这种方式不需要了解,不可能使用。

客户端模式

        纯后台方案。client拿着client_id和secret去换access_token。通常就是我们后台服务调用的时候用到。比如使用aws的s3或者阿里云的oss上传文件。我们需要通过ak,sk认证,获取一个token,拿token去上传文件。这个流程写起来挺麻烦,一般都会封装好客户端给我们用。