如果你在进行getphonenumber操作的流程之前,执行过wx.login的登录操作,调用你们自己后台自己写的登录接口之后 再去进行getphonenumber 的操作的话,在 getphonenumber 的回调里就不要再次调用wx.login的api拿最新的code了,因为这样每次点击获取电话号码都会获取最新的code ,但是code有两个特点:1.有效期只有5分钟,超过5分钟即失效 2.获取最新的code有可能会刷新你登录时候的登录态,导致登录时候获取的sessionkey失效,这就是每次点击getphonenumber按钮实时获取code有一定几率失败的原因,会报 bad key 的错误,如果在登录接口调用之后 点击getphonenumber的动作之前的这个时间段再次获取最新的code保存到本地,这样也会有code失效的问题 invaild code 由于getphonenumber的按钮没有做防重复 在ios上特别的明显,快速点击的时候会弹出两个授权框,也有可能再执行了第一个getphonenumber的操作 调转到了页下个页面之后,弹出改授权框,你再点击就会报code been used 或者直接卡死在跳转后的页面,这个问题没有办法修复,只能依靠微信官方来解决了,以上这两种情况都不适合,那么该怎么处理哪,我的思路是 在点击getphonenumber的按钮之后,在按钮的bindtap事件中让一个wx.showToast加载框显示(防止多次重复弹框,能改善,不能彻底解决),在弹出的授权框点击允许的动作之后才会真正的执行 bindgetphonenumber的回调,在该回调中先调用wx.checksession方法检验sessionkey的有效期,如果有效提示后台使用调用登录接口时候code获取的sessionkey去配合 点击点后号码的回调事件中的iv 和encryptedData,去进行解密电话号码的操作,如果过期则再调用wx.login 获取最新的code去拿到有效的sessionkey,但是根据我的测试,这个时候仍然会报错,也就是说一旦登录时候的sessionkey被刷新了,不管你如何操作(重新code也不行) 再解密电话号码都会报错的。目前只有靠官方来处理了。
解密用户信息报错pad block corrupted解密用户信息报错,如下: 前端: [代码] [代码][代码]wx.login({[代码] [代码] [代码][代码]success: [代码][代码]function[代码] [代码](loginResp) {[代码][代码] [代码][代码]if[代码] [代码](loginResp.code) {[代码][代码] [代码][代码]//获取用户信息[代码][代码] [代码][代码]wx.getUserInfo({[代码][代码] [代码][代码]withCredentials: [代码][代码]true[代码][代码],[代码][代码] [代码][代码]success: [代码][代码]function[代码] [代码](res) {[代码][代码] [代码][代码]//封装登陆需要的参数[代码][代码] [代码][代码]var[代码] [代码]params = {};[代码][代码] [代码][代码]params.code = loginResp.code;[代码][代码] [代码][代码]params.encryptedData = res.encryptedData;[代码][代码] [代码][代码]params.iv = res.iv;[代码][代码] [代码][代码]//调用登陆接口[代码][代码] [代码][代码]that.sendRequest({[代码][代码] [代码][代码]url: [代码][代码]'/login'[代码][代码],[代码][代码] [代码][代码]data: params,[代码][代码] [代码][代码]success: [代码][代码]function[代码] [代码](res) {[代码][代码] //cache userInfo...[代码][代码] [代码][代码]}[代码][代码] [代码][代码]})[代码][代码] [代码][代码]},[代码][代码] [代码][代码]fail: [代码][代码]function[代码] [代码](e) {[代码][代码] [代码][代码]console.log(e);[代码][代码] [代码][代码]}[代码][代码] [代码][代码]});[代码][代码] [代码][代码]} [代码][代码]else[代码] [代码]{[代码][代码] [代码][代码]console.log([代码][代码]'获取用户登录态失败!'[代码] [代码]+ res.errMsg)[代码][代码] [代码][代码]}[代码][代码] [代码][代码]}[代码][代码] [代码][代码]});[代码] 后台步骤: 1) 跟据code换取用户session_key 拼接url参数:https://api.weixin.qq.com/sns/jscode2session?appid=xxx&secret=xxxx&js_code=xxxx&grant_type=authorization_code 请求方式:http get 返回结果: {"session_key":"xxxxxxxx","openid":"xxxxxxxxxx"} 2) 使用解密工具类解密出明文,使用:AES/CBC/PKCS7Padding AES方式解密 参数:encryptedData、sessionKey、iv 结果: javax.crypto.BadPaddingException: pad block corrupted 但是紧接着第二次再走以上流程是可以正常解出明文用户信息的 过会再走以上流程第一次请求又是报这个错误
2019-12-03 wx.checkSession({ success (){ //session_key 的状态设置为 ‘1’(这个是在本地设置的) 未过期,并且在本生命周期一直有效 发送请求获取电话号码 that.getPhone('', '1' , e.detail.iv, e.detail.encryptedData, that); console.log('success'); }, fail (){ console.log('fail'); // session_key的状态设置为 ‘0’ 已经失效,需要重新执行登录流程 获取新的code wx.login({ success(res) { if (res.code){ that.getPhone(res.code,'0', e.detail.iv, e.detail.encryptedData, that); } } }) } }) 说明:that.getPhone(code, sessionKeyValidFlag, iv, encryptedData,that); 是定义一个通用的方法去调用获取电话号码的接口 code 为 在wx.checkSession方法失败的时候 说明session_key失效了就要从 wx.login实时获取 如果成功 则 设置code为空 让后台使用 第一次登陆的session_key sessionKeyValidFlag是根据 wx.checkSession 的成功失败 手动设置的 最关键的是不要频繁去调用 wx.login 有可能会刷新登陆态,导致解密失败。
session_key作用问题会话密钥session_key有效性开发者如果遇到因为session_key不正确而校验签名失败或解密失败,请关注下面几个与session_key有关的注意事项。 wx.login()调用时,用户的session_key会被更新而致使旧session_key失效。开发者应该在明确需要重新登录时才调用wx.login(),及时通过登录凭证校验接口更新服务器存储的session_key。 微信不会把session_key的有效期告知开发者。我们会根据用户使用小程序的行为对session_key进行续期。用户越频繁使用小程序,session_key有效期越长。 根据文档知道,每次wx:login()之后都会更新session-key,又有: [代码]wx.checkSession({ success: function(){ //session_key 未过期,并且在本生命周期一直有效[代码] [代码] [代码] [代码]wx:[代码]DecryptParentInfo(......); //解密用户信息 [代码] }, fail: function(){ // session_key 已经失效,需要重新执行登录流程 wx.login() //重新登录 .... } })[代码]现在想知道的是,界面用户信息时,需要用到session_key去解密iv、encryptedData,对此,同一小程序不同的用户都用session_key去解密iv、encryptedData时,每一个用户的session_key是否一致,说白了就是解密时,这个session_key是否所有用户都是共用的。。?????还是说每一个用户解密时,都是不同的session_key(各自用各自的session_key)?????? 因为,出现了两个问题: 1)如果共用session_key的话,是否会出现一个问题:一个用户wx:checkSession()时没有过期,再去解密用户信息,而另一个用户刚好在解密之前再去登录刷新了session_key,此时session_key已改变,此时第一个用户解密将会失败,导致无法解密。。。。 2)如果不共用session_key的话,wx:checkSession()没过期后再用session_key在后台执行解密方法,大部分都是解密成功,偶尔出现了解密失败的情况,请问一下此时是什么问题导致的解密失败的。。。。。??????因为,发现一个问题就是并发去登录时,将会频繁出现解密用户信息失败,求大神帮忙感激不尽。。。。。。。。。。。。。。 最后想明确两个问题: 1)session_key是否解密时共用的(同一个session_key去解密不同的用户信息)? 2)解密失败的根本原因????????
2019-11-29我来解答下吧,这问题我也遇到过,问题出在哪里呢,就出在点击 button 的地方,没有 wx.login 得到code 的时候,iv和encryptedData 就已存在一个session_key ,和之后得到的session_key 就不同了,我第一的逻辑是在点击button按钮的时候重新获取code,发送请求给服务器,让服务器去解密新的session_key和opendid 这样会有一定的几率导致 登录态session_key 更新,后台会报 解密微信用户信息异常 :javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption 当然这个报错是有一定的概率的,这就是楼主的情况 。 我之后想过 登录之后再调用一次 wx.login 方法 获取下code 再等点击获取电话号码按钮的时候 把这个code传递给后台,但是这个code 会有 5分钟的有效期 超过5分钟就会报 invalid code 失效的code 最后想到了 微信的api wx.checksession方法,但是该方法只会去校验 session_key是否有效,并不能去校验code 是否过期 ,由于session_key的过期时间官方没有给,code的过期时间只有5分钟,所以还得转变思路,就是在使用wx.checksession的时候在成功的回调里 说明session_key是有效的,所以就给后台传递一个有效的标识,让后台使用之前的session_key,带上点击事件e里面获取的iv 和 encryptedData 来解密用户的电话号码 在失败的回调里,说明这个时候的session_key失效了,就走楼主的逻辑,重新调用wx.login方法,获取最新的code 交给后台去解密有效的session_key,最终解决该问题 //如果用户点击确定就采取的操作 if (e.detail.errMsg === 'getPhoneNumber:ok') { //测试会话是否有效 code是否过期 wx.checkSession({ success(){ //session_key 未过期,并且在本生命周期一直有效 发送请求获取电话号码 usesessionkey为session_key是否过期的标识 getPhone 是通用的进行解密电话号码的方法 that.getPhone(that.data.usesessionkey, e.detail.iv, e.detail.encryptedData, that); console.log('success'); }, fail(){ console.log('fail'); // session_key 已经失效,需要重新执行登录流程 获取新的code 发送给后台去获取最新的sesson_key wx.login({ success(res) { if (res.code){ that.getPhone(res.code, e.detail.iv, e.detail.encryptedData, that); } } }) } }) }
小程序getPhoneNumber 解密手机号时,偶尔会出现填充无效,无法被移除客户端 getPhoneNumber: function (e) { let that=this; wx.login({ success: function (res) { if (e.detail.errMsg == 'getPhoneNumber:fail user deny') { wx.navigateTo({ url: '../../pages/userLogin/userLogin', }); } else { if (e.detail.errMsg == 'getPhoneNumber:fail:cancel to confirm login') { wx.navigateTo({ url: '../../pages/userLogin/userLogin?url='+that.data.url }); } else { //console.log(res.code); wx.request({ url: api.AESdecrypt, data: { 'encryptedDataStr': e.detail.encryptedData, 'code': res.code, 'iv': e.detail.iv }, method: 'post', header: { 'content-type': 'application/x-www-form-urlencoded' }, success: function (data) { console.log(data); app.globalData.userInfo = data.data.data; setTimeout(function(){ wx.navigateBack({ changed: true });//返回上一页 },500); }, error: function () { tip.showToast('error'); }, fail: function (err) { tip.showToast(err); } }) } } } }); }, 服务端 try { string encryptedDataStr = System.Web.HttpContext.Current.Request["encryptedDataStr"], code = System.Web.HttpContext.Current.Request["code"], iv = System.Web.HttpContext.Current.Request["iv"]; ReturnData resultMsg = new ReturnData(); resultMsg.state = (int)StatusCodeEnum.Success; resultMsg.message = StatusCodeEnum.Success.GetEnumText(); string strresult = ""; string Appid = "Appid"; string Secret = "Secret "; string grant_type = "authorization_code"; GetUsersHelper GetUsersHelper = new Controllers.GetUsersHelper(); //向微信服务端 使用登录凭证 code 获取 session_key 和 openid string url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Appid + "&secret=" + Secret + "&js_code=" + code + "&grant_type=" + grant_type; Stream s_re = WebRequest.Create(url).GetResponse().GetResponseStream(); StreamReader sr = new StreamReader(s_re, Encoding.UTF8); string strLine = sr.ReadToEnd(); sr.Close(); //将字符串转换为json格式 JObject jo = (JObject)JsonConvert.DeserializeObject(strLine); result res = new result(); try { //微信服务器验证成功 res.openid = jo["openid"].ToString(); res.session_key = jo["session_key"].ToString(); } catch (Exception) { //微信服务器验证失败 res.errcode = jo["errcode"].ToString(); res.errmsg = jo["errmsg"].ToString(); WriteInfo(res.errmsg); } if (!string.IsNullOrEmpty(res.openid)) { //用户数据解密 strresult = GetUsersHelper.AESDecrypt(encryptedDataStr, res.session_key,iv);//.AES_decrypt(encryptedDataStr,res.session_key,iv);// JObject jos = (JObject)JsonConvert.DeserializeObject(strresult); ResultInfo ress = new ResultInfo(); ress.phoneNumber = jos["phoneNumber"].ToString(); var model = Ioc.Get<IUserRepository>().FindMobile(ress.phoneNumber); resultMsg.data = model; } else { strresult = null; resultMsg.data = strresult; } return resultMsg; } catch (Exception ex) { WriteInfo(ex.Message); return new ReturnData { state=200, message="error", data=null }; } 解密.net public string AESDecrypt(string inputdata, string AesKey, string AesIV) { try { RijndaelManaged rijalg = new RijndaelManaged(); //----------------- //设置 cipher 格式 AES-128-CBC rijalg.KeySize = 128; rijalg.Padding = PaddingMode.PKCS7; rijalg.Mode = CipherMode.CBC; rijalg.Key = Convert.FromBase64String(AesKey); rijalg.IV = Convert.FromBase64String(AesIV); byte[] encryptedData = Convert.FromBase64String(inputdata); //解密 ICryptoTransform decryptor = rijalg.CreateDecryptor(rijalg.Key, rijalg.IV); string result; using (MemoryStream msDecrypt = new MemoryStream(encryptedData)) { using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read)) { using (StreamReader srDecrypt = new StreamReader(csDecrypt)) { result = srDecrypt.ReadToEnd(); } } } return result; } catch (Exception ex) { ApiUserController apis = new ApiUserController(); apis.WriteInfo("jiemi"+ex.Message); return null; } }
2019-11-28