什么是OAuth2
我们都知道,很多网站登录的时候,可以通过微信、QQ、微博等APP扫码扫码认证登录,其实这就是OAuth2的应用。
但是这里我想说明的是oauth2是一种授权许可。它的核心本质是用于授权,获取用户的资源信息,而不是用于登录。登录是它的附加产物。这个细节我们一定要理解清楚,以便于后续理解整个OAuth2时,不会让自己无法理清它的核心逻辑。
OAuth2的几种不同的“角色”
在这里让我们用“掘金”这个网站的登录来举例子吧!
- 资源拥有者:掘金的用户
- 第三方软件:掘金(有些文章叫做客户端)
- 授权服务:微信开放平台的授权服务
- 受保护的资源:微信头像、微信其他信息
OAuth2的几种类型
- 授权码凭据许可
- 资源拥有者凭据许可(账号密码类型)
- 客户端凭据许可
- 隐式许可(简单类型)
授权码凭据许可
官方流程
不知道你们是什么感受,反正我看官方的图,我觉得我理解能力有限,不知道整个流程到底是咋样的。
掘金登录流程
我们还是以掘金网站为案例,以下是登录掘金的全流程:
- 用户打开掘金
- 用户点击登录(微信登录)
- 界面跳转到微信扫码页面
- 用户扫码登录
- 用户授权并同意
- 登录成功,页面跳转回掘金
掘金登录详细流程
以上是我们用户能感知到的全流程,但是整个流程的服务交互,我们仍然不知道。下面我再以服务交互的角度,更详细描述下整个登录流程:
- 用户打开浏览器,打开掘金
- 用户点击登录
- 页面跳转到微信扫码页面
- 微信授权服务校验掘金的请求信息
- 用户用微信扫码
- 用户用微信授权并同意
- 微信授权服务校验用户信息
- 页面跳转到掘金前端页面并附带上授权码
- 掘金前端用授权码请求掘金后端
- 掘金后端调用微信的OpenApi请求访问令牌
- 微信授权服务校验授权码及掘金的请求信息,并响应访问令牌
- 掘金后端拿到访问令牌,并通过访问令牌获取用户信息(用户唯一ID、微信头像,用户名等)
- 掘金后端记录用户登录
- 掘金后端响应前端用户登录成功
- 掘金前端给用户展示登录成功页面
通过以上来看,整个流程真的挺复杂的,步骤繁多。所以我用时序图给大家画出来这一切的交互
我们需要重点关注的几个步骤
-
第三点与第四点(跳转到微信扫码登录页)
当我们从掘金跳转到微信扫码页面的时候,我们可以观察下微信扫码页面的URL
https://open.weixin.qq.com/connect/qrconnect?appid=wx1f78f78832fc2c16&redirect_uri=https%3A%2F%2Fjuejin.im%2Fpassport%2Fauth%2Flogin_success&response_type=code&scope=snsapi_login&state=0d26ae7d3gASoVCgoVPZIDU5OTlkODY2ZDg1MTA1YmJkYjhjOTY4NzI4Y2VkMzYzoU6-aHR0cHM6Ly9qdWVqaW4uaW0vb2F1dGgtcmVzdWx0oVYBoUkAoUQAoUHRCjChTdEKMKFIqWp1ZWppbi5pbaFSBKJQTNEDQaZBQ1RJT06goUyyaHR0cHM6Ly9qdWVqaW4uaW0voVTZIDJjNzMzMWI5YzdkMzk5MzljYjYyYjdiODllOTJlNDZioVcAoUYAolNBAKFVww%253D%253D#wechat_redirect
URL中有三个关键参数
- appid:appId,第三方的唯一标识
- redirect_uri:授权成功后的跳转URL
- scope:权限范围
关于appId,这里稍微补充一下。当掘金需要利用微信做第三方授权登录时,掘金需要去微信的开放平台上面“注册”。这个“注册”操作,其实就是申请appId、appSecret、填写授权成功后的跳转URL、以及掘金的权限范围。
当我们跳转到微信扫码登录页的时候,会把appId、授权成功后的跳转URL、权限范围作为参数,这时微信的授权服务其实会根据appId进行一系列的校验:
- appId是否正确(你不能随便瞎传一个appId,否则微信怎么知道是否是合法的第三方呢?)
- 根据appId校验授权成功后的跳转URL是否正确(如果跳转的URL不是注册时填写的URL,是不被允许的)
- 根据appId校验第三方的权限范围是否正确(第三方本次授权的权限范围不能超过它申请时的范围。比如申请时只需要查询用户信息,但是本次授权却需要查询用户好友列表,这肯定是不被微信允许的)
当验证信息通过后,微信授权页面才会打开,展示用户扫码的页面。
-
第七点(校验用户授权信息)
当用户用微信授权并同意的时,这时用户会提交授权同意信息给微信授权服务。这时,微信授权服务主要做三个任务:
- 验证权限范围:第三方软件可能需要用户个人信息、用户好友列表、用户所在地等权限信息,但是作为用户为了保护自己的隐私,不一定会把全部的信息给到第三方,用户可以自由选择哪些允许授权。
- 生成授权码:当用户同意授权后,微信授权服务会为本次授权生成授权码。
- 重定向至第三方URL:微信授权服务授权码成功后,需要告知第三方软件,它要通过跳转到第三方的URL上。
-
第八点(授权码)
授权码是一个非常关键的信息,它用于后续与appSecret配合,获取访问令牌。关于授权码,我们要知道的是:
- 授权码是一次性的,用了一次之后,微信会把它作废,后续想要使用,必须使用新的授权码。
- 授权码是有时间限制的,而且这个时间是非常短的,官方推荐是10分钟以内,而大部分授权平台授权码的有效时间一般是3~5分钟。
这里授权码生命周期设计的如此短,而且它是一次性的,主要是为了安全。因为授权码是微信通过重定向跳转到第三方URL上的,所以授权码是直接暴露在外的。
-
第十点(访问令牌)
掘金想要访问到用户的资源,最终还是要通过访问令牌去请求用户的资源。授权码其实只是一个临时的通行证。获取访问令牌有三个关键参数:
- appId:用户的唯一标识,微信授权服务需要校验第三方是否存在
- appSecret:还记得上述我们说过,掘金需要去微信开放平台注册时的申请的appSecret吗?就是在这里用上了。
- 授权码:第八点生成的授权码
微信根据上述三个参数,校验第三方是否存在,appSecret是否正确,授权码是否正确来生成访问令牌。
至于访问令牌的种类,不同的软件授权服务有不同的规则,它可以是一串UUID,也可以是JWT,我们日常使用的token都适合的。只不过大家要注意的是,OAuth2和JWT其实并没有绝对依赖的关系,不要一开始就把二者混为一谈,否则后续很容易让自己云里雾里。
当掘金拿到访问令牌后,就可以访问用户资源接口,获取用户信息了。
其实很多人不理解,为什么要弄一个授权码还弄一个访问令牌呢?直接一步到位不就好了吗?
我们仔细看看时序图,授权码是微信通过浏览器的URL重定向告诉掘金的,所以基本上没啥安全性可言,大家都能知道它;
而访问令牌则是掘金的后端服务器直接与微信授权服务通信,获取到的,因此它的安全性是比较好的。
另外,有些软件授权服务不仅仅会返回访问令牌(accessToken),还会返回一个刷新令牌(refreshToken)。刷新令牌的作用其实和授权码很相似,就是通过刷新令牌获取访问令牌,而且访问令牌也是一次性的,也有有效期,只不过它的有效期会相对比较久,有些软件的刷新令牌有效期是一周。
为什么有这个刷新令牌呢?因为访问令牌是有有效期的。假设没有刷新令牌,当访问令牌过期后,如果第三方软件还要继续获取用户资源信息,那么只有一个办法了:告诉用户访问令牌过期,让用户重新走一遍访问令牌申请流程。毫无疑问,这个用户体验是非常差的。而如果有刷新令牌,那么第三方软件可以再访问令牌过期前,在后端静默的申请一个新的访问令牌,而整个流程是用户无感知的。
大厂获取accessToken的时序图
-
微信
-
支付宝
资源拥有者凭据许可(账号密码类型)
关于资源拥有者凭据许可,我们可以换个方式想象一下。假设,微信把掘金收购了,也就是掘金变成了微信的,那么微信账号密码直接告诉给掘金也没关系,反正大家都是一家人,也不存在什么账号密码泄露的问题,用同一套登录服务就行了。
那么用户就能直接用微信的账号密码获取访问令牌,后续掘金获取用户资源直接利用访问令牌就可以了。
客户端凭据许可
客户端凭据许可这个类型的应用场景,其实主要是“资源拥有者被塞进了第三方软件中” 或者 “第三方软件就是资源拥有者”。它主要是通过appId与appSecret获取访问令牌直接访问用户资源。
大家可以想象一下“云存储服务器”。比如“七牛云存储”、“阿里云OSS”,我们可以用我们自己编写的软件,访问我们的云盘。而我们作为资源拥有者,与我们自己的软件合二为一。而且我们的软件与“七牛云存储”、“阿里云OSS”是直接通过后端交互访问的,所以安全性会比较好,可以直接通过appId与appSecret获取访问令牌。
隐式许可(简单类型)
这种类型其实应用的非常少,主要是用于第三方软件只有前端,没有后端的情况。因为只有前端,所以第三方软件直接嵌入浏览器中,通过浏览器与授权服务交互。这种情况,基本上就没有所谓的安全性可言了,因此也不需要appSecret了,前端直接通过appId就可以获取到accessToken。