单页面应用在微信服务号下的登录流程
最近我们的小程序涉及到虚拟支付的问题,在ios端的支付被封掉了😇,所以有了在服务号上搞一套H5版的小程序的需求。由于我们小程序是mpvue写的,为了尽量复用之前的样式和逻辑,选择了前后端分离的模式,于是一段新的踩坑之旅开始了。放下wx的jssdk暂且不表,今天来说说登录时遇到的坑。
服务号的登录流程
以前搞过服务号的同学对于它的登录流程应该不陌生,就是当后端检测到当前用户没有授权时,将会重定向到微信的授权页面,当用户点击这个授权的button时,微信会根据Url查询字符串中的重定向URL,重新回到我们的页面。
下面3幅图展示了整个过程:
-
当我们的服务器发现用户没有授权,返回
302
状态码,以及微信的授权页面location -
用户在该页面点击确认登录
-
微信服务器根据location里的redirectURL,返回
301
code,重定向回来
上面三步,经历了两次重定向,第一次从自己的服务器重定向到微信的服务器,微信展示授权页面。第二次重定向是当用户点击之后,微信会带一个code重定向回来,当服务器拿到这个code之后,经历一波获取openId的操作之后,生成一个session,这样用户以后访问时就不需要再次登录。
这样的模式在传统的前后端不分离,基于模板的情况下,是没有问题的,因为没有json的返回,后端进行逻辑处理后,渲染出html。但是在单页面的情况下,如果思路跑偏会出现一堆问题。
单页面遇到的问题
上面说的思路跑偏是什么意思呢?就是当用户进来时,先将单页面的index.html
发给浏览器。当浏览器执行开始js脚本(app.js)时,就会向服务器发送请求。
此时如果是一个新用户在访问,由于没有登录,服务器会返回一个302的重定向状态码,然而这次请求是通过ajax发起的。浏览器不会自动重定向到授权页面,导致请求失败。
此时你会想,浏览器不会重定向,我可以当请求失败时通过设置window.location
自己重定向到微信授权页面,这样解决了第一步的重定向问题,然后当用户点击确定登录时,马上又面临第二个重定向的问题--重定向到哪里。
由于微信重定向的url是带着code返回的,重定向的Url肯定不能是一个静态页面的Url,必须是一个api,假设还是login。后端在请求中拿到code之后,生成一个新的session,再将原先的html再次返回给浏览器,并带上set-cookie
字段,此后浏览器会带着cookie请求,登录至此完成。
这样做ok,但是却留下一个很恐怖的URL:https://example.com/login?code=001QLbSQ0Ujc162Sp5UQ0IG6SQ0QLbSD
,此后前端路由开始work,就在前面的基础上加上一个#
号,如果你的业务还涉及支付,那就完蛋了,因为支付需要配置的url应该是稳定的而code这个查询参数是动态变的。
解决方案
我画了一张图解释了重新设计的登录流程:
在这个新的流程中,当新用户第一次发起请求(login)时,不会返html给浏览器,只会进行重定向:
-
如果用户未登录,重定向到微信的授权页,并设置redirect url,使用户点击授权后可以重定向回来
-
新用户重定向回来之后,login api拿到code,经过生成session操作后,再次重定向到
home
url下 -
如果是老用户,直接重定向到
home
url下 -
此时由于访问
home
的用户都是已登录状态,api统一返回index.html,所以最终用户看到的是https://example.com/home
下的页面,后面的单页面路由会在这个url下展开。(虽然对服务号用户来说可能没什么区别)
总结一下这么做的好处:
-
少传输一次html页面,第一种方式一开始就给未登录的用户传html是没用的
-
美观的url(用户感知不到)
-
前端无需手动重定向,后端将显示页面和登录逻辑放到两个api中,逻辑更清晰
总结
了解到3xx
状态码的博大精深,尽管对普通用户感知不深,没有2xx
受群众欢迎,也没有404
,500
知名度高,属于默默无闻型的。但是在互联网世界里却扮演着重要的角色。