小程序审核突然没通过,理由如下:
这个问题开发过程中自己确实遇到过,几率性的,一般第一次不行,第二次肯定可以了,但是不是一开始写小程序就有的,不知道什么时候开始就这样了,验证的逻辑都是按照官方的,从来没有改变过。然后上社区一搜,很多类似的问题,如下图所示。
看了下这个问题,第一次验证签名如下:
小程序端通过wx.login成功后获取的code
rawdata,这个我都是同一用户登录,前后信息没啥变化
通过1中的code,后端调用api获得的session data,其中openid肯定同一用户每次也都一样的,session_key如果过期,那么第一次和第二次理论应该是不一样的。(但实际情况前后两次是一致的,具体可参见下图)
小程序端获取到的用户的签名
后端通过session key校验出来的签名。
很明显,4和5不一致,校验失败。接下来是第二次交验:
还是同样的逻辑顺序。
小程序端通过wx.login成功后获取的code。很明显,code跟第一次是不一样的,另外根据官方文档描述,因为又重新调用了wx.login,会导致session_key过期。(这似乎说明code发生变化也是对的,因为按推测,seesionkey应该也发生了变化,否则怎么叫“被更新”)请看下图官方文档说明:
rawdata,这个我都是同一用户登录,前后信息没啥变化
根据1中的官方描述,奇怪的现象就发生了,在后端根据新的code,获取的session data,很明显session key还是第一次是一样的,也就是说,我重新调用了wx.login, code是变了,但是session key却和第一次保持一致的。
小程序端获取到的用户的签名
后端通过session key校验出来的签名。因为用的是同样的rawdata,同样的session key,所以两次校验的结果是一样的,但是第二次4中,小程序端获取的签名是跟此次校验结果是一致的。
所以问题就来了,这问题到底出在什么地方?似乎官方文档描述的就有问题,还是我本身的逻辑顺序有问题?请官方指教,谢谢。
code 的作用只是用来换取 session_key,从你提供的信息来看,不会是 code 的问题,只可能是 session_key 的问题。有个问题需要注意:
验签使用的 session_key,必须和小程序端获取到的签名是配对。因此这里需要你确认下,获取 3 和 4 的时序关系是怎样的?预期应该是这样的逻辑:
获取 3(wx.login + jscode2session 拿到 session_key)
获取 4(wx.getUserInfo 拿到 signature)
使用 1、2 拿到的结果来验签
如果你的服务器缓存了 session_key,在 session_key 未过期的情况下(可用 wx.checkSession 确认),可以跳过 1 。假设你用的是很久以前拿到的 signature + 刚获取到的 session_key 来验签,那就有可能不通过。
非常感谢您的回答,那问题应该就是我步骤错了,我是先wx.getUserInfo 拿到 signature,然后再wx.login + jscode2session 拿到 session_key,最后才验签的,您说的1,2我反掉了。非常感谢。
实在不好意思,对于您的解答我还是有个疑问,需要麻烦问下。就是您说的1和2步骤,我不知道会不会出现这样的情况,就是我完成1步骤后,出于某种原因(因为至少目前开发者是没法获知以及控制sessionkey的有效期的),sessionkey过期了,然后现在开始执行步骤2,那么我拿到的用户签名肯定也是无效的,对吗?这样我去验签肯定会失败。而因为目前小程序wx.getUserInfo必须通过<button open-type="getUserInfo"></button>这样的形式,让用户主动点击才能获取对吗?就算是wx.authorize({scope: "scope.userInfo"}),也无法弹出授权窗口。这样问题就来了,为了验证签名,如果这种情况发生,是不是势必需要用户第二次点击这个按钮才能正确验签?如果这样的话,体验就很差了,还是说,我始终哪里没搞明白?麻烦帮忙解答下,谢谢。
这个问题我来回答一下楼主。
为了保证wx.getUserInfo不会因为session_key过期而失败。微信对于用户session_key过期的情况做了预生成session_key的机制。
所以在你的流程“我是先wx.getUserInfo 拿到 signature,然后再wx.login + jscode2session 拿到 session_key,最后才验签的”
首先调用的wx.getUserInfo,签名所对应的session_key,对于小程序的老用户而言是不可预知的,因为用户之前的session_key可能有效也可能已经失效。这样可能会导致你签名校验失败。
建议先调用 wx.checkSession先行获取session_key有效期(注意必须在wx.getUserInfo之前调用),并且在过期的情况下调用wx.login获取最新的session_key。
那么……我倒是有问题了
如果说用户进入小程序的时候就调用了wx.login,经过了很长一段时间(此时session_key已经过期),用户点击了某个登陆按钮调起了getUserInfo,这种情况下就是无法解密的?
因为现在的产品逻辑下,getUserInfo的时机不是一个可控的时机,毕竟是用户触发的,但是触发回调只有finish回调而没有will回调,也就是说获得了signature之后才知道自己的session有没有过期
至于为什么不是可控时机,就要问你们自己废弃wx.getUserInfo的时候是怎么想的了……
补充说明一点:wx.getUserInfo 并不是直接废弃,只是废弃了其首次获得用户授权的能力。如果已经通过 button 得到用户的授权(可通过 wx.getSetting 判断),后续都是可以直接用 wx.getUserInfo 接口不弹窗获取到用户信息的。
参考流程代码
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 换 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'
)
}
}
}
});
我感觉目前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个不同名字的官方人员。。真心求助
小游戏改名后,米大师中应用名称不能同步,求协助
Sky.Wu昨天 15:5716
- 当前 Bug 的表现(可附上截图)
小程序改名后,米大师中应用名称不能同步,求协助,着急
appid:wx0c9ea6721d1da882
我提的问题,也经常石沉大海,那个开发者工具在笔记本上不能使用的问题,几年了 还没解决
你这条帖子是我见过评论最长回复最长的一条帖子。
哈哈,也是没办法,一开始提的有点情绪,但作为开发者,确实是想切切实实的能解决问题,不管是哪一方的问题,开发者千千万万,总归会有各种重复的或者甚至在他们官方眼里是弱智的问题,但从开发者角度讲,还是希望能得到有效的帮助,而不是互嘲,总之我相信,不管谁写的代码,总有错的。当然你提到的,很多人提到的为什么官方基本都没回复的,我想下面的截图多多少少能解释下这个问题:
这是官方人员的解答。
很可能我们的问题已经被定义在了8%中。
但是微信方面开发,官方支持是出了名的难找!!大概比使用开源的东西提issues还难,效率还低
谢谢您这么及时的回答问题,我姑且认为是回答。首先这个发帖人我完全不认识,所以说明这个问题似乎不需要定向来排查吧?很多不一样的开发者都遇到了相同的问题,当然不说100%排除是我们这些人犯了同样的错误,我希望也是我们犯了错,因为毕竟你们比我们牛,几乎不会出错,而且我们错了更容易改,对吧?另外你们在提供这些相对敏感的信息时候,有保密的文本吗?直接贴在公开的论坛里让别人看到appid和openid?虽然这不是secret,但总感觉有点怪怪得啊,一般其他平台,提供这些敏感信息,都是有保密的字段可以填写的,只有你们官方可以看到,不是吗?另外既然您说了要appid,那我这正好有2个,我想说的是这2个都同时有这个问题。另外我比较弱鸡,还是请麻烦解答下,直接这么公开的提供appid和openid没啥安全隐患吧?再次感谢您专业的帮助。
wx345ace1cb60e9fdf
wxaa0ddc4b1f7e3156
o_WKt4rN6LTl6aQR2Fb9ax_35zZU
刷新下页面,我修改了回答。
谢谢,抱怨提问我是承认的,因为个人感觉,你们既然已经有了这么好的经验,也看到了别人的问题,为什么不直接回答下呢?而是直接忽略?或者你们针对这样“线上线下我们定位过非常多次”的问题,有什么相关的排查文档可以查看?我相信,专业我们这些无知的开发者也不会让你们感到厌烦不是吗?我还是想说下自己的看法,虽然可能还是错的,也不怕您的耻笑。
1用错code,请高手明示,何为用错了code?code是直接通过wx.login成功返回的。
2重复用 code,我上面的提问中已经截图了,2次code不同
3解密算法错,同一解密算法,要错就没对的时候吧?
4用错 session key,同样何为用错seesion key?用的是这个地址
https://api.weixin.qq.com/sns/jscode2session
加appid secret加上code获取的。
提问要诚恳,如果有让您觉得不开心的,我表示抱歉,我也完全能接受是我自身的问题导致的,但是还是希望您能帮忙解答下,谢谢。
抱歉,补上解密部分,就2句话,
const crypto = require('crypto');
const sha1 = crypto.createHash('sha1').update(fullUserInfo.rawData + sessionData.session_key).digest('hex');
您好,我有一个比较急的疑问
-------------
之前通过 `wx.getUserInfo()` 获取用户信息时,它有个前提条件要求先 `wx.login()` 而且未过期。现在换成通过按钮触发的形式后,是在哪一个阶段获取授权code呢?是在点击按钮之前就要获取到code?还是在bindgetuserinfo的回调函数内部获取code?
在bindgetuserinfo的回调函数内部获取的code,会不会是无效的?(我在体验版是有效的),以为部分JS源码(mpvue):
<script>
export
default
{
methods: {
async onGetUserInfo (res) {
// bindgetuserinfo的回调函数
const that =
this
const { userInfo, errMsg, encryptedData, iv } = res.target
if
(errMsg ===
'getUserInfo:ok'
) {
wx.login({
success (loginRet) {
if
(loginRet[
'code'
]) {
that.loginAfter({
userInfo,
encryptedData,
iv,
code: loginRet[
'code'
]
})
}
},
complete (loginRet) {
if
(!loginRet[
'code'
]) {
wx.showModal({ title:
'授权失败'
})
}
}
})
}
},
loginAfter ({ userInfo, encryptedData, iv, code }) {
// do something
}
}
}
</script>
我们问的应该是同一个问题,在回调里再获取code,此时的UserInfo很可能就已经是无效的了。但是不知道能否直接在回调函数里,直接再wx.getUserInfo一次,如果可以,这个问题也能暂时完美的解决掉,马上测试下,呵呵。