目录

1 问题描述

小程序成功登陆后,安卓用户预约操作时,偶尔会出现登陆异常情况。

2 登陆实现方案

后端Cookie校验用户登陆状态

3 排查过程

1)后台日志排查,用户Cookie无效

2)微信小程序日志排查,安卓用户有问题

3)现象复现,安卓手机提示异常

4)微信小程序埋点输出日志,Cookie存取正常

5)正常与异常Cookie对比,Cookie顺序不对

6)异常Cookie排查,Cookie拼接不正确

4 为什么苹果手机、部分安卓手机没有问题?

5 问题原因

腾讯小程序Bug,Set-Cookie逗号拼接方式有问题

6 解决方法

Cookie正则分割,分号重新拼接

7 标准答案

8 硬广告

微信预约小程序,14天免费使用


1 问题描述

最近有用户反馈,小程序登陆有问题

小程序成功登陆后,安卓用户预约操作时,偶尔会出现登陆异常情况。

之前解决过shiro登陆失败的问题,《Shiro框架Given final block not properly padded问题解决》,但是没有彻底解决登陆失败的问题。

登陆失败现象极其诡异,问题难以复现。

1 用户10秒前刚登陆,用户后续操作马上提示“登陆异常”

2 部分安卓用户在登陆后,在后续操作提示“登陆异常”,这种现象是偶然发生的,安卓用户出现登陆异常概率大概是10分之1

2 苹果用户从未没有出现”登陆异常“现象。

axios 微信小程序登陆 异常验证 微信小程序登录异常_登陆

 

2 登陆实现方案

在介绍问题前,本文先简单描述下本文应用登陆实现方案。

本文的小程序应用是单独使用Cookie来维护登陆状态,登陆并未使用小程序的sessionKey来维护后台登陆状态。

后端Cookie校验用户登陆状态

为什么不使用小程序SessionKey维护登陆状态?

1 老应用有单独Cookie登陆方式。

2 后台支持多平台登陆(比如小程序、h5网页等),不完全依赖小程序SessionKey完成登陆 。

Shiro控制用户权限

axios 微信小程序登陆 异常验证 微信小程序登录异常_Cookie_02

1 获取微信小程序code值

2 code值以及appId换取用户openId

3 openId快捷登陆

4 小程序保存登陆Cookie

5 小程序携带Cookie请求后端应用

6 后端根据前端的Cookie校验用户的登陆情况

 

可惜小程序不支持Cookie,(浏览器一般会保存用户Cookie,方便后续浏览网页使用)

如何解决小程序支持Cookie登陆?

以下是网上常见的Cookie登陆解决方式。

// 登录
      wx.login({
        success: function (res) {
          log.info(res)
          //获取登录的临时凭证
          var code = res.code;
          //调用后端,获取微信的session_key,secret
          wx.request({
            url: domain + '/user/wxLogin',
            header: {
              "Content-Type": "application/x-www-form-urlencoded"
            },
            method: "POST",
            data: util.json2Form({
              code: code,
              appId: that.globalData.appId
            }),
            success: function (result) {
              // cookie存储起来
              var cookie = result.header['Set-Cookie']
              wx.setStorageSync('cookie', cookie)
              
            },
            fail: function (res) {
              
            }
          })
        }
      })
    })

wx.request从res.header['Set-Cookie']中获取cookie信息,并使用wx.setStoargeSync将cookie信息同步写入小程序私有存储空间中

wx.request({
      url: domain + '/api/xxx/xxx',
      header: {
        "Content-Type": "application/x-www-form-urlencoded",
        'cookie': wx.getStorageSync("cookie")
      },
      method: 'POST',
      data: util.json2Form({ xx1: xx1, xx2: xx2 }),
      success: function (result) {
        
      },
      fail: function (res) {
        log.info("服务器异常:" + res)
        wx.showToast({
          title: '服务器异常',
          icon: 'none',
          duration: 2000
        })
      }
    })

wx.getStorageSync同步将cookie从小程序中取出,小程序携带该cookie请求后台服务,完成用户登陆状态校验。

 

3 排查过程

问题不可怕,可怕的是不能稳定地复现case。本人平时使用iphone手机,重复用户的操作,基本复现不出来“登陆异常”的现象,只能在服务后台以及小程序前端疯狂埋点打日志。

1)后台日志排查,用户Cookie无效

从后台日志可以看出,用户成功登陆,但是用户没有在后续的请求使用用有效cookie去请求后台服务,后台直接拒绝服务。

axios 微信小程序登陆 异常验证 微信小程序登录异常_登陆_03

2)微信小程序日志排查,安卓用户有问题

通过后台和小程序日志发现,出现“登陆异常”的用户基本是安卓用户,苹果用户基本没有这个问题。

 

3)现象复现,安卓手机提示异常

目标锁定安卓手机,直接找了台安卓手机,重复登陆、预约操作,大概反复操作10次,终于复现了“登陆异常”的现象,喜大普奔啊。

 

4)微信小程序埋点输出日志,Cookie存取正常

起初怀疑wx.setStoargeSync有bug,未将cookie成功写入小程序。

验证方式:将res.header['Set-Cookie']、wx.getStorageSync日志输出。

如果res.header['Set-Cookie']输出日志为空,则定位是后端服务问题,未将cookie携带返回。

如果res.header['Set-Cookie']输出日志不为空,wx.getStorageSync('cookie')输出日志为空,则定位setStorageSync写入有bug

但是最终现象res.header['Set-Cookie']、wx.getStorageSync('cookie')输出日志均不为空,排查思路中断了。

 

5)正常与异常Cookie对比,Cookie顺序不对

对比正常请求的cookie与异常请求的cookie

发现cookie的顺序不一样

后台shiro做权限控制,返回3个Set-Cookie值,苹果用户的请求基本是以JESSIONID开头,而有问题的安卓用户请求是以remeberMe=Delete开关

axios 微信小程序登陆 异常验证 微信小程序登录异常_安卓手机_04

 

6)异常Cookie排查,Cookie拼接不正确

将登陆异常请求的cookie按照正常请求cookie的顺序调整了下, 用户请求后端可以成功登陆。

使用错误顺序cookie请求后端,后端解析cookie并未获取到JESSIONID。

后端是如何解析Cookie,根据分号";"分割Cookie字符串,由于错误顺序Cookie是使用逗号做分割符,后端将Expires与JESSIONID视为一个整体,所以无法解析出JESSIONID

axios 微信小程序登陆 异常验证 微信小程序登录异常_登陆_05

4 为什么苹果手机、部分安卓手机没有问题?

没有问题的Cookie,JESSIONID可以被后台正确分割出来。苹果手机获取Set-Cookie顺序是严格一致的,安卓手机Set-Cookie顺序是随机的。

 

5 问题原因

腾讯小程序Bug,Set-Cookie逗号拼接方式有问题

微信开放社区也有人反馈过这个bug,但是以微信社区人员解决问题的稀烂态度,至今未解决该bug。

解决bug只能靠自己去兼容这个bug

axios 微信小程序登陆 异常验证 微信小程序登录异常_安卓手机_06

 

6 解决方法

当返回结果的header有多个Set-Cookier时,微信小程序获取res.header['Set-Cookie'],已经是将Set-Cookie用逗号拼接好的字符串。

如果使用简单的逗号分割,Expires时间也含有逗号,Cookie结果是错误的。

Cookie正则分割,分号重新拼接

见示例

axios 微信小程序登陆 异常验证 微信小程序登录异常_登陆_07

简单的逗号无法分割,可以使用正则表达式去分割Cookie字符串,

需要被分割的逗号,后继字符串是含有=

 

7 标准答案

微信小程序使用Cookie登陆标准答案

// 登录
      wx.login({
        success: function (res) {
          log.info(res)
          //获取登录的临时凭证
          var code = res.code;
          //调用后端,获取微信的session_key,secret
          wx.request({
            url: domain + '/user/wxLogin',
            header: {
              "Content-Type": "application/x-www-form-urlencoded"
            },
            method: "POST",
            data: util.json2Form({
              code: code,
              appId: that.globalData.appId
            }),
            success: function (result) {
              // Set-Cookie字符串获取
              var cookie = result.header['Set-Cookie']
              // 字符串分割成数组
              var cookieArray = cookie.split(/,(?=[^,]*=)/)
              // 分号拼接数组
              var newCookie = cookieArray.join(';')
              // 存储拼接后的cookie
              try {
                wx.setStorageSync('cookie', newCookie)
              } catch (error) {
                log.error('setStorageSync cookie fail')
              }
              
            },
            fail: function (res) {
              
            }
          })
        }
      })