单页面应用在微信服务号下的登录流程

最近我们的小程序涉及到虚拟支付的问题,在ios端的支付被封掉了??,所以有了在服务号上搞一套H5版的小程序的需求。由于我们小程序是mpvue写的,为了尽量复用之前的样式和逻辑,选择了前后端分离的模式,于是一段新的踩坑之旅开始了。放下wx的jssdk暂且不表,今天来说说登录时遇到的坑。

服务号的登录流程

以前搞过服务号的同学对于它的登录流程应该不陌生,就是当后端检测到当前用户没有授权时,将会重定向到微信的授权页面,当用户点击这个授权的button时,微信会根据Url查询字符串中的重定向URL,重新回到我们的页面。

下面3幅图展示了整个过程:

  1. 当我们的服务器发现用户没有授权,返回302状态码,以及微信的授权页面location

    1.png

  2. 用户在该页面点击确认登录

    uploading-image-28683.png

  3. 微信服务器根据location里的redirectURL,返回301code,重定向回来

    2.png

上面三步,经历了两次重定向,第一次从自己的服务器重定向到微信的服务器,微信展示授权页面。第二次重定向是当用户点击之后,微信会带一个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这个查询参数是动态变的。

解决方案

我画了一张图解释了重新设计的登录流程:

uploading-image-511687.png

在这个新的流程中,当新用户第一次发起请求(login)时,不会返html给浏览器,只会进行重定向:

  • 如果用户未登录,重定向到微信的授权页,并设置redirect url,使用户点击授权后可以重定向回来

  • 新用户重定向回来之后,login api拿到code,经过生成session操作后,再次重定向到homeurl下

  • 如果是老用户,直接重定向到homeurl下

  • 此时由于访问home的用户都是已登录状态,api统一返回index.html,所以最终用户看到的是https://example.com/home下的页面,后面的单页面路由会在这个url下展开。(虽然对服务号用户来说可能没什么区别)

总结一下这么做的好处:

  1. 少传输一次html页面,第一种方式一开始就给未登录的用户传html是没用的

  2. 美观的url(用户感知不到)

  3. 前端无需手动重定向,后端将显示页面和登录逻辑放到两个api中,逻辑更清晰

总结

了解到3xx状态码的博大精深,尽管对普通用户感知不深,没有2xx受群众欢迎,也没有404,500知名度高,属于默默无闻型的。但是在互联网世界里却扮演着重要的角色。

相关文章