1、前言javascript
说到OAuth,先来一段百度到的比较官方的解释:html
OAUTH协议为用户资源的受权提供了一个安全的、开放而又简易的标准。与以往的受权方式不一样之处是OAUTH的受权不会使第三方触及到用户的账号信息(如用户名与密码),即第三方无需使用用户的用户名与密码就能够申请得到该用户资源的受权,所以OAUTH是安全的。OAuth是Open Authorization的简写。java
说实话,每次见到官方定义的东西 都想git
(╯' - ')╯︵ ┻━┻ (掀桌子) ┬─┬ ノ( ' - 'ノ) {摆好摆好) (╯°Д°)╯︵ ┻━┻(再掀一次)
单从字面意思来说,就是Open Authorization,即 开放 受权的意思。官方解释就是官方,看了几遍仍然不知道是什么东西,因此今天博主也根据本身的理解,彻底用白话的方式,不掺杂一点官方语言来解释下OAuth2.0。用你的亲身经历举个栗子来讲 就是 如今你用到的第三方登陆,好比qq、微信、微博登陆啊,免去了本身注册帐号的烦恼,实际上这就是OAuth的实现和解决的问题。OAuth也经历了几个版本,目前到了2.0版本,也就是咱们常说的OAuth2.0。浏览器
2、为何要有OAuth安全
是为了解决问题啊(这不是废话吗,出来一个东西,确定是要解决问题的),可是到底解决什么问题呢,再举个栗子,不够吃了。服务器
2.1 解决用户身份认证问题微信
咱们每当去访问一个网站的资源的时候,网站都会要求咱们注册一个帐号,访问资源的时候登陆帐号来表名咱们的身份,注意,这一步仅仅就是为了表名你的身份,知道你是个正常的人,而不是一些机器什么的。你对网站有什么操做,网站也能很好地监测到。post
这样一来,你每当去访问一个新的网站的时候都要注册帐号,对用户来讲,麻烦,反正我是不想注册了,对网站来讲,我得记录你的帐号密码什么的,也麻烦。(网站主要目的仍是为了肯定你的身份)。网站
那么问题来,能不能有一种方式,帮网站作认证来表名你是个正常的人,好比能够找一些比较出名且有权威的大公司大网站来认证一下,如qq,微博什么的,由于这些大公司已经存储了你的身份,若是你访问个人网站可让大公司来证实一下你的身份,我知道
你是谁就能够了,也不用你在个人网站注册帐号了。这样一来,你方便,我也方便。因此,OAuth2.0 给出了规范,来解决认证问题。
2.2 解决第三方网站访问已有的用户资源的问题
如今有这么一种状况,既然前边已经说了,你能够用你的qq,微博帐号登陆个人网站,那么我又有其余非分之想了,我想获取到你在qq或者微博上昵称啊来显示到个人网站上,这样感受高大上一点,更有甚者,我还想要。。。。emmm ,不是那个了。
我还想要其余东西,我想读取下你的微博,说说啊什么的,或者我也想你能在个人网站发表这些东西。这样岂不是更加高大上。可是这些功能都是qq和微博提供的,我怎么能获取到权限呢。
一个办法就是 你把你的qq或者微博的帐号和密码给我,我去登陆,这样我就能像你同样操做各类东西。可是你可能就不肯意了,个人帐号密码都给你了,万一你把我上边的小片什么的都泄漏出来,我还怎么才朋友里边作人,万一你装做我搞一些坏事情,我岂不是更要身败名裂,毕竟这是最高权限啊。还有,我这qq密码说不定就是其余各类帐号的密码,你给我存起来,登陆个人支付宝什么的,那怎么行。反正,就是一大堆不行。可是我要获取你qq和微博上的东西你总得给我吧,因此为了解决这个问题。OAuth就又出来了,给出来规范,来解决资源受权问题。
3、OAuth解决上述问题的流程
很明显,解决上述问题,须要三我的的参与,因此每一个角色之间都得存守必定的约定和规则,这一套规则就是OAuth2.0协议的规范,具体比较官方的规范你们本身去看,本文就是以白话文的方式来说解的,不会有那种官方的条条框框。
3.1 第三网站a与qq的协议(只以qq举例子,其余都同样)
首先qq会对他上边的用户权限进行分类,好比说获取头像,获取昵称,修改昵称,读取说说,发表说说之类的权限,都会进行整理,当a网站要获取那些用户的权限的时候就会颁发给他相应的权限,固然为了肯定是a网站来访问我大企鹅用户的东西,会要求a网站在qq平台上注册一下身份,好比说用client_id和client_sercret来表名a网站应用的身份,若是是b网站 想访问也要注册一下,未来qq也会知道是b网站来访问个人用户上的资源和权限。
固然,为了更加明确和安全,qq还会要求a网站注册一个地址(redirect_url)或者注册你须要获取用户的那些权限,这样一来,你来的时候,个人检查一下redirect_uri是否是你的,当你让qq的用户登陆受权成功以后,我也好根据redirect_url跳回去找你啊。
3.2 用户和qq的协议
用户来进入a网站,a网站集成了“用qq登陆“登陆的功能,因而用户点击用qq登陆,那么a网站就会带上client_id去跳转到qq的受权页面,qq一看client_id就知道是a网站来的,核对一下(经过client_id)这个a网站是qq受权过的客户端,我根据client_id找出a网站能够获取的权限,给用户显示出来,而后让用户选择你是否赞成a网站获取你的这些权限。以下图,这里我以码云 的qq登陆为例
咱们能够看一下这个页面的地址:https://graph.qq.com/oauth2.0/show?client_id=101284669&redirect_uri=https%3A%2F%2Fgitee.com%2Fauth%2Fqq_connect%2Fcallback&response_type=code&state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0
1. 红色部分是qq的受权地址,绿色部分是码云携带的参数,很明显码云也向qq注册了,有client_id和redirect_uri两个重要参数,redirect_uri用户赞成受权后跳回去,明显是码云的一个地址。
2. response_type=code 这个参数是OAuth2.0规范,意识是受权方式为code方式,此处通常填写固定值code,由于还有其余受权方式,可是不经常使用,
3. state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0 这个也是里边的规范 状态参数 值是一个随机字符串,须要qq原样返回给码云的,置于什么做用,参考好多资料,都没有明确指出,你们也只是遵照这个规范(不过确定有用的,如今还不清楚)
注意:当用户没有赞成,OAuth的受权流程就结束了。若是赞成了,才继续进行。
因此这一步流程就本就是,你带着参数向qq的受权页面跳转 https://graph.qq.com/oauth2.0/show?&client_id=xxxx&redirect_uri=xxxxx&response_type=code&state=xxxx 红色部分是你须要填写的部分。你们能够参看微信网页受权就是酱紫的。
3.3 qq的处理而且返回给a网站
qq确定是做记录啊,记录用户对码云授予了这两个权限,未来出什么问题也好作一个凭证啊,万一你来讹qq一下说,我没有受权,你怎么把个人东西给码云了。(马云:管我什么事!!)记录以后,会生成一个code码,这个code确定是和用户的受权记录是一一对应的,这个code码也是OAuth2.0的规范,而且这个code码有过时时间,而且只能用一次(置于为何这么设计,先插个眼咱们后边说)。而后你们能够看后续的流程:
3.3.1 首先,qq会给我发个提示:说我已经受权了,表名qq已经知道了,而且也已经留取证据了,你就不可能反咬qq一口了,这也是OAuth2.0设置这一步的目的所在。整个三个角色的态度都会有记录。
3.3.2 网址以下:https://gitee.com/auth/qq_connect/callback?code=FA0E3CBB6B699D894EE2BFD85691A561&state=f70af08d82d762a56d844e0b0f1d0b7abafd48127c5b4ee0 很明显能够看到,这个地址是以前码云填写的redirect_uri的地址,这里qq跳转回来,并携带上了code码,说明用户已经赞成了你的受权,至于state能够看见仍是原样返回的。
3.4 用code获取access_token
上一步中,码云已经获取到了code码,但这个 code 只能代表,用户容许码云从 qq上获取该用户的数据,若是我直接拿这个 code 去 qq访问数据必定会被拒绝,由于任何人均可以持有 code,qq并不知道 code 持有方就是码云。
因此换取access_token的时候就要码云本身也携带着本身向qq申请的帐号密码来换取了,这里边已经在界面上看不到了,是再跳转的过程当中默默进行的。
POST https://www.oauth.qq.com/login/oauth/access_token //去请求qq获取access_token的地址 通常都是post params = { code: "xxx", client_id: "xxx", client_secret: "xxx", redirect_uri: "https://gitee.com/auth/qq_connect/callback" }
固然若是一切正常,qq会给你返回以下结果
response = { access_token: "e72e16c7e42f292c6912e7710c838347ae178b4a" scope: "username,headimg" //这个是能够获取用户的权限的范围,是前边用户选择的部分,这里举个例子,表示只容许获取用户名和头像 token_type: "bearer", refresh_token: "e72e1dedfs42f2923112e7710csafgsdaklfhalrf1352371537838347ae178b4a" }
3.5 拿着access_token去调用接口
有access_token了还不随心所欲啊,获取个昵称,获取的头像,没事发个说说,读个微博啥的。到此OAuth2.0 受权流程基本就结束了。固然这个access_token也是有时间的,通常为两个小时。过时的话用返回的refresh_token刷新,由于在请求接口的过程当中传输的是access_token且只有两小时的有效时间,即便是被别人截获了,损失也不会太大,设计两个小时的过时是为了安全考虑的。由于这一步的access_token是访问接口的惟一凭据,并不会判断调用者的来源。因此是有可能被非法者截获搞出一些事情的。
4、总结
1.关于code码的问题:
Q:为何不直接发放access_token而非要返回一个code码,而后用code换取access_token,这不是屡次一举吗?
A:首先有这个想法很好,证实你是一个有思想的同窗。当初我也想了好半天。由于qq根据要跳回你的网站才行,而跳转到你的网站是302重定向,只能是get请求,因此要传递给码云一些数据信息的时候必须在url中添加参数,若是这时直接把access_token传递回来,就会暴露出来,这样就很不安全,并且是依赖浏览器的,因此加了一部分,只把code码传回来,这样即便code被暴露了也没问题,由于码云要在再次用code码和你的client_id,client_secret来请求qq,qq会作验证,而这一步就能够不依赖浏览器了,你能够在服务器用代码模拟http请求获取acess_token,这样就比较安全了。
Q:为何code要设计成一次性的?
A:假若code能无限次用,那么当用户在上述状况下收回了权限,可是因为code还能用,自己code又关联了用户的受权信息,因此码云可能再次用code来换取token,这一步并无通过用户的容许是违法的。因此不能让code继续使用。一次code表名了用户的一次意愿,并非终生的意愿。置于code为何要设置有效期,我想应该是你若是获取了code不使用,qq也不会说一直帮你存着吧。
另外,当受权码发放以后,qq确定也确定添加并记录了用户当前对哪一个网站发放了受权,这个记录也为后来的用户收回权限作准备,若是用户要收回权限,直接对相关记录操做就行。以下,是qq的受权管理:
2.关于OAuth的标准协议
有关OAuth的标准协议,各大厂商的内部实现可能不尽相同,有时候传的参数多多少少会有一点出入,可是大致流程上都是这样,并且有些必须参数都是同样的。
协议的官方内容能够参考阮一峰老师的 理解OAuth 2.0。另外一篇 简述 OAuth 2.0 的运做流程 也很不错。