小程序验证签名(登录)的流程(含官方解答的最佳实践)
发布于 4 年前 作者 xiuying68 4250 次浏览 来自 问答

小程序审核突然没通过,理由如下:

这个问题开发过程中自己确实遇到过,几率性的,一般第一次不行,第二次肯定可以了,但是不是一开始写小程序就有的,不知道什么时候开始就这样了,验证的逻辑都是按照官方的,从来没有改变过。然后上社区一搜,很多类似的问题,如下图所示。

看了下这个问题,第一次验证签名如下:

  1. 小程序端通过wx.login成功后获取的code

  2. rawdata,这个我都是同一用户登录,前后信息没啥变化

  3. 通过1中的code,后端调用api获得的session data,其中openid肯定同一用户每次也都一样的,session_key如果过期,那么第一次和第二次理论应该是不一样的。(但实际情况前后两次是一致的,具体可参见下图)

  4. 小程序端获取到的用户的签名

  5. 后端通过session key校验出来的签名。

很明显,4和5不一致,校验失败。接下来是第二次交验:

还是同样的逻辑顺序。

  1. 小程序端通过wx.login成功后获取的code。很明显,code跟第一次是不一样的,另外根据官方文档描述,因为又重新调用了wx.login,会导致session_key过期。(这似乎说明code发生变化也是对的,因为按推测,seesionkey应该也发生了变化,否则怎么叫“被更新”)请看下图官方文档说明:

  2. rawdata,这个我都是同一用户登录,前后信息没啥变化

  3. 根据1中的官方描述,奇怪的现象就发生了,在后端根据新的code,获取的session data,很明显session key还是第一次是一样的,也就是说,我重新调用了wx.login, code是变了,但是session key却和第一次保持一致的。

  4. 小程序端获取到的用户的签名

  5. 后端通过session key校验出来的签名。因为用的是同样的rawdata,同样的session key,所以两次校验的结果是一样的,但是第二次4中,小程序端获取的签名是跟此次校验结果是一致的。

所以问题就来了,这问题到底出在什么地方?似乎官方文档描述的就有问题,还是我本身的逻辑顺序有问题?请官方指教,谢谢。

8 回复

我感觉目前getuserinfo的button存在bug,并不是session的问题

说下我的流程:

1、页面onload,里wx.checksession,fail了就wx.login,第一次打印log输出未过期

2、页面加载后立刻点击getUserInfo的button

3、getUserInfo的回调中,首先wx.login(这个实际上不需要,这里只是为了复现bug),获取code

4、发送code和button回调中的rawData和签名A到服务端

5、服务端验签失败,输入log中,sessionkey为A

6、立刻第二次点击(第二步),此时checksession依然输出未过期

7、发送新code和统一的rawData还有签名B到服务端,注意这里的签名B肯定跟刚才的签名A不一样

8、服务端验签成功,log中显示,sessionkey依然为A

第二次尝试,中间增加wx.getUserInfo:

1、页面onload,里wx.checksession,fail了就wx.login,第一次打印log输出未过期

2、页面加载后立刻点击getUserInfo的button

3、getUserInfo的回调中,首先wx.login(这个实际上不需要,这里只是为了复现bug),获取code

4、调用wx.getUserInfo,打印log,输出签名 W1

5、发送code和button回调中的rawData和签名A到服务端

6、服务端验签失败,输入log中,sessionkey为A

6、立刻第二次点击(第二步),此时checksession依然输出未过期

7、发送新code和统一的rawData还有签名B到服务端,注意这里的签名B肯定跟刚才的签名A不一样

8、调用wx.getUserInfo,打印log,注意输出签名仍为 W1

9、服务端验签成功,log中显示,sessionkey依然为A

结论是:

1、在一个生命周期内,多次调用wx.login并不会改变code换回来的sessionkey

2、在wx.login中,button和wx.getUserInfo返回的签名不同,但多次点击,每一次的button和button是一样的,wx.getUserInfo和wx.getUserInfo也是一样的,但是一小段时间后,就都会换新的签名了

3、但是2中有个问题,如果页面刷新,过一会点击,这时候button返回的签名是无效的,wx.getUserInfo是有效的!再次点击,button的签名换了(这时候是有效的),wx.getUserInfo的签名没变,这时候也是有效的

4、最简单的处理办法是button回调里面再调wx.getUserInfo一次,用wx.getUserInfo的信息去请求服务端,此方式比较繁琐但没有任何问题


具体还要看官方人员排查,开发者也要维护要checksession和wx.login,不要频繁login

为什么我一个米大师应用名称同步的问题,上礼拜五到现在都没人可以帮我们解决呢? 在这里却看到了3个不同名字的官方人员。。真心求助

小游戏改名后,米大师中应用名称不能同步,求协助

问题模块框架类型问题类型操作时间AppID
管理后台小游戏Bug昨天 00:00wx0c9ea6721d1da882

- 当前 Bug 的表现(可附上截图)

小程序改名后,米大师中应用名称不能同步,求协助,着急

appid:wx0c9ea6721d1da882

你这条帖子是我见过评论最长回复最长的一条帖子。

我提的问题,也经常石沉大海,那个开发者工具在笔记本上不能使用的问题,几年了 还没解决

code 的作用只是用来换取 session_key,从你提供的信息来看,不会是 code 的问题,只可能是 session_key 的问题。有个问题需要注意:

验签使用的 session_key,必须和小程序端获取到的签名是配对。因此这里需要你确认下,获取 3 和 4 的时序关系是怎样的?预期应该是这样的逻辑:

  1. 获取 3(wx.login + jscode2session 拿到 session_key)

  2. 获取 4(wx.getUserInfo 拿到 signature)

  3. 使用 1、2 拿到的结果来验签

如果你的服务器缓存了 session_key,在 session_key 未过期的情况下(可用 wx.checkSession 确认),可以跳过 1 。假设你用的是很久以前拿到的 signature + 刚获取到的 session_key 来验签,那就有可能不通过。

参考流程代码

authPromise = new Promise(async (resolve, reject) => {
  while (!authorization.token) {
    try {
      const data = await WXP.login();
      let res;
      try {
        if (canAutoLogin) {
          res = await getToken(data.code);
        } else {
          throw Error('try user info');
        }
      } catch (e) {
        canAutoLogin = false;
        const set = await WXP.getSetting();
        if (!set.authSetting['scope.userInfo']) {
          throw Error('scope userInfo disabled');
        }
        const user = await WXP.getUserInfo({
          withCredentials: true,
          lang: 'zh_CN',
        });
        // [@todo](/user/todo) 换 token 的时候出现了错误需要处理
        try {
          res = await getToken(data.code, user.iv, user.encryptedData);
        } catch (err) {
          throw err;
        }
      }
      authorization.token = res.data.access_token;
      authPromise = null;
      // 顺便获取一次
      store.dispatch('user/USER_GET_PROFILE')
      resolve();
    } catch (e) {
      const set = await WXP.getSetting();
      const router = Vue.prototype.$router;
      if (!set.authSetting['scope.userInfo']) {
        router.push('/pages/user/login');
        try {
          await new Promise((res, rej) => {
            Vue.bus.on('ON_USER_LOGIN', (action, data) => {
              if (action === true) {
                res(data);
              } else {
                rej();
              }
            });
          });
        } catch (err) {
          // 暂时先什么都不做 用户必须授权
          // authPromise = null;
          // reject(err);
          // throw err;
        }
        Vue.bus.off('ON_USER_LOGIN')
      }
    }
  }
});

谢谢您这么及时的回答问题,我姑且认为是回答。首先这个发帖人我完全不认识,所以说明这个问题似乎不需要定向来排查吧?很多不一样的开发者都遇到了相同的问题,当然不说100%排除是我们这些人犯了同样的错误,我希望也是我们犯了错,因为毕竟你们比我们牛,几乎不会出错,而且我们错了更容易改,对吧?另外你们在提供这些相对敏感的信息时候,有保密的文本吗?直接贴在公开的论坛里让别人看到appid和openid?虽然这不是secret,但总感觉有点怪怪得啊,一般其他平台,提供这些敏感信息,都是有保密的字段可以填写的,只有你们官方可以看到,不是吗?另外既然您说了要appid,那我这正好有2个,我想说的是这2个都同时有这个问题。另外我比较弱鸡,还是请麻烦解答下,直接这么公开的提供appid和openid没啥安全隐患吧?再次感谢您专业的帮助。

wx345ace1cb60e9fdf

wxaa0ddc4b1f7e3156

o_WKt4rN6LTl6aQR2Fb9ax_35zZU

回到顶部