前言
当我们的产品存在多端的特性时,Web端的移动端扫码登录需求就会变得很常见,即使没有移动端的产品,也可以借助第三方的移动端产品进行扫码登录,这样做的好处在于减少因为繁杂的注册登录流程造成的用户登录流失。下面让我们基于自平台扫码登录和第三方平台扫码登录进行方案的分析,看看到底是如何实现的。
基本流程
在了解前端扫码登录的流程之前,我们先梳理一下常规的账号密码登录认证做了什么:一般我们会传递一组账号密码到服务器,它们用于向服务器告知和证明我的身份,如果认证通过,服务器会返回给我们一个 鉴权token(也可能是session方案),表示登录成功的同时用于后续访问,我们的终极目标就是这个token。
二维码扫码登录需要则额外借助移动端设备,其前提是移动端已经登录了我们的账号并通过了登录认证,此时我们已经有了一份登录凭据存储在移动端。那么我们要做的就是通过这份登录凭据,告诉服务器允许我们在 PC 端的登录行为,并将token传递给PC端。
且由于移动端并不能和 PC 端直接通讯,所以就使用了二维码作为信息载体进行。
自平台扫码登录
自平台扫码登录即为PC 端和移动端都是自己的产品,在整个过程中有三个角色,PC端
,移动端
,服务器
。
我们可以把一次扫码登录看成一次事务,PC端
去服务器
创建一个未完成的登录事务,再委托移动端
到服务器
完成该事务。可以分解为以下几步:
PC端
首先需要生成一个随机 ID 绑定这次事务(生成也可以服务端去完成),传递到服务器
,表明自己想进行扫码登录的操作。服务器
根据这个随机 ID,生成一个未完成的登录事务保存在本地,随后返回相关信息到PC端
PC端
将返回的信息生成二维码,等待移动端
扫码完成事务,在此期间不断轮询服务器
,询问事务是否完成移动端
扫描二维码,根据上面的信息将自己的登录凭据传递过去服务器
收到了移动端
的登录凭据,校验通过后将该事务标记为已完成,下次PC端
轮询时,即可将登录 token 返回,完成本次扫码登录
以上过程可以总结为下面的时序图:
二维码生成
由于前端不能和移动端直接通讯,所以采用了二维码作为信息媒介,那么二维码中就需要注意不能包含敏感信息,且要保证移动端扫码后可以获得去服务端
完成认证的所有信息,一般必要的内容有:
- 随机数 ID 用于绑定登录事务
- 过期时间
- 相关接口信息
前端可以通过一些插件生成二维码,例如QRCode.js
插件实现二维码生成,具体参考官方仓库。
前端完成登录
在移动端
扫码后,PC端
如何获悉已经完成了登录认证是一个问题,算上我们之前提到的轮询,一共有四种方案:
- 轮询
- 长轮询
- SSE
- websocket
轮询
轮询即为前端设定定时器,循环向服务器发送请求,询问状态是否更新。轮询是实现最为简单,兼容性最好,但性能欠佳的一种方法,想要快速响应就要缩短定时器时间,大量的HTTP请求十分不优雅
长轮询
长轮询是基于轮询的基础上做的优化,当服务器收到轮询请求后,会将请求挂起,不会立即回复前端,在F12调试台中可以看到请求未完成,是挂起状态。等到数据发生变化时,才会发送 response 响应,如果期间一直没有数据变化,则达到超时时间后会发生超时响应,前端收到超时响应后会进行下一次长轮询。
长轮询主要由服务端实现,优点是减少了轮询产生的 http 请求数量,缺点是服务端挂起也会导致资源浪费,但总体来讲尚可。
SSE(eventsource)
eventsource
是 HTML5 的一种新特新,是一种基于 http 协议的实时通讯方案,不同于 websocket 可以可以双向通讯的特性,SSE 是单工的,即只能由服务器推送消息。
其实现的原理是通过 http 与服务器建立一个持久化连接,content-type
为text/event-stream
,从服务器订阅了一个事件流,服务器通过这这条流向客户端推送消息。
SSE 的优点是实现简单,不需要建立或者保持大量的请求,节省了很多资源。其唯一的缺点是 IE 不支持,不过 IE 已经是时代的眼泪了
websocket
前端还可以通过与服务端建立 websocket
来及时获取扫码登录成功的消息。其优点是更强大和灵活,因为他是全双工通讯;但对比 SSE,其全双工的特点在扫码登录场景下并无太大的用处,有种拿大炮打蚊子的感觉,因为我们不需要向服务器传递消息,仅需要等待扫码成功的消息即可。其缺点则是实现方案比 SSE 更为复杂,这同时体现在前后端上,后端还需要一个专门的 websocket 服务支持,因为我们采用的是另一种协议
总结
总结上面的四种方案,从兼容性与实现简易度,从高到低分别是:
- 轮询 > 长轮询 > SSE > websocket
而从功能的强大排序,则分别是:
- websocket > SSE > 长轮询 > 轮询
可以根据自己的实际情况来进行选择
第三方平台扫码登录
第三方应用扫码登录,即移动端
变成了第三方软件,且需要访问第三方服务器来完成登录认证,这个过程中有四个角色:PC端
,服务器
,第三方移动端
,第三方服务器
。目前最流行的授权登录机制是 OAuth2.0
,微信,QQ,微博等第三方平台都是采用此机制,我们基于此进行介绍:
第三方扫码登录,PC端
真正需要的内容,是自己服务器
上的鉴权 token 以及第三方服务器
上的用户信息。
首先是用户信息,在使用第三方登录功能时,一般需要到第三方登录开放平台进行申请,比如微信开放平台,在申请通过后会返回给你AppID
和AppSecret
。在PC端
准备进行第三方登录时,我们需要带着AppID
,跳转到第三方提供的扫码页面,由第三方移动端
扫码授权登录,授权成功后该第三方网页会携带授权码重定向到我们自己的页面。整个过程由第三方服务器
全自动完成,我们只需要配置好携带参数跳转,就能在重定向后拿到用于获取用户信息的授权码。这一步可以看做第三方平台自己实现了前面介绍的自平台登录,这里的AppId
就相当于随机事务ID,并把这个系统里的扫码登录页面拿来给我们用
随后PC端
携带授权码请求我们自己的服务器
,后者使用该请求码帮我们去访问第三方服务器
以获取用户信息后。获取成功后连带着鉴权 token,一并返回给PC
端,如此便登录成功,整个流程结束。
还是以微信第三方登录为例,以上过程可以总结为下面的时序图:
对比自平台扫码登录
第三方平台扫码登录,对比自平台扫码登录,实际上仅多了获取用户信息的过程,这个过程又分为获取授权码和由授权码获取用户信息。
在获取授权码这一步,我们没有直接和第三方服务器
交互,而是第三方服务器
通过一个扫码登录页
自己实现了前面的自平台扫码登录的流程,我们只需要携带参数跳转,就能拿到授权码,十分方便。但有时候新打开一个页面进行扫码登录不是很优雅,微信也提供了相关组件,供我们不跳转在页面内登录,假如一些第三方没有提供,采用 iframe 也是个可行方案
在微信提供的扫码登录页上,打开 F12 调试,可以看到微信采取的是长轮询的策略
拿到授权码后,再通过我们自己的服务器
,去第三方服务器
获取用户信息,再连带我们的鉴权 token 一并返回
为什么返回授权码,而不是直接返回 access_token
在拿到授权码后,可以看到服务器先是请求了 access_token,之后再用 access_token 请求的用户信息,相当于多了授权码这个中间步骤。
OAuth2 多出的这一步是为了安全,授权码是通过 302 重定向携带参数返回的,有可能被截获,如果此时传递的是用户信息的直接凭证 access_token,就可能导致用户信息泄露,但攻击者即使拿到了授权码,他也无从得知获取 access_token 的参数AppSecret
。
使用授权码还有一个好处,可以在获取 access_token 的时候使用 https 加密,在授权成功 302 跳转回源站点的时候,很难保证所有使用第三方平台的源站点都支持 https,这也是其不安全的原因。但第二步获取 access_token 是由源站点请求第三方平台,后者可以保证自己支持 https
结语
作者能力有限,以前端视角出发讨论第三方扫码登录的实施,作为学习总结记录,如有架构大佬欢迎勘误