前面我不是整理了一套微信小程序開發的api啊,原由是由於一個朋友接了一個私活,咱們一塊兒幫忙開發,而後再這個小程序裏面有一個功能,就是經過微信小程序獲取用戶手機號後端處理,來看一下這個功能在微信官方是怎麼定義的前端
注:程序員在本身的技術能力提高以後,閒來無事接一些小的開發任務,不沉重,可是久而久之,其實真的賺的很多,去吃頓好吃的旅個遊不香嗎?java
好了,言歸正傳程序員
微信官方網頁對於微信小程序獲取用戶手機號的處理描述以下redis
這頁主要是對前端作法的描述,主要描述了前端應該提早經過wx.login登錄,或者進行登陸態檢查,以此避免刷新登陸態的操做,避免出現服務端存的sessionKey不是最新的sessionKey從而出現敏感數據解密失敗的問題。前端經過button觸發bindgetphonenumber事件,拿到加密數據傳給後端,後端經過解密算法解密。算法
微信會對這些開放數據作簽名和加密處理。開發者後臺拿到開放數據後能夠對數據進行校驗簽名和解密,來保證數據不被篡改。因此
一、前端經過調用接口(如 wx.getUserInfo)獲取數據時,接口會同時返回 rawData、signature,其中 signature = sha1( rawData + session_key )
二、開發者將 signature、rawData 發送到開發者服務器進行校驗。服務器利用用戶對應的 session_key 使用相同的算法計算出簽名 signature2 ,比對 signature 與 signature2 便可校驗數據的完整性。apache
後端拿到rawData後經過SHA1()算法對後端存儲的sessionKey進行SHA1(rowData,sessionKey)加密,若是獲得的signature與前端傳來的signature一致,則校驗成功。(獲取用戶手機號時數據校驗不是必須的,因此此處只敘述邏輯並未實現)小程序
官方文檔對數據解密的解釋是這樣的:
接口若是涉及敏感數據(如wx.getUserInfo當中的 openId 和 unionId),接口的明文內容將不包含這些敏感數據。開發者如須要獲取敏感數據,須要對接口返回的加密數據(encryptedData) 進行對稱解密。解密算法以下:
對稱解密使用的算法爲 AES-128-CBC,數據採用PKCS#7填充。
對稱解密的目標密文爲 Base64_Decode(encryptedData)。
對稱解密祕鑰 aeskey = Base64_Decode(session_key), aeskey 是16字節。
對稱解密算法初始向量 爲Base64_Decode(iv),其中iv由數據接口返回。後端
因此筆者進行數據解密時採用了網絡上搜的解密工具使用
我獲得簡潔的解密工具以下:微信小程序
`public class AesUtil { public static String wxDecrypt (String encrypted, String sessionKey, String iv)throws Exception { byte[] encrypData = Base64.decodeBase64(encrypted); byte[] ivData = Base64.decodeBase64(iv); byte[] sKey = Base64.decodeBase64(sessionKey); String decrypt = decrypt(sKey,ivData,encrypData); return decrypt; } public static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception { AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //解析解密後的字符串 return new String(cipher.doFinal(encData),"UTF-8"); } } ` * *
按照常理來講(以小編神奇的99%運氣+1%實力)對比官網進行開發,一把不會出現問題,可是,沒想到,陰溝裏翻船了(最後得知bug緣由的我被無情嘲諷)api
代碼在測試階段出現了問題,拋出「BadPaddingException: Given final block not properly padded. Such issues can」開頭的異常
通過問題排查,找出問題
一、微信小程序開發者工具獲取的sessionKey有效期爲5分鐘,過期以後sessionKey就會解不出來
二、完善解密算法,增長處理異常的try–catch塊,更換getInstance參數
解決上述兩個問題以後,優化了工具,拿到用戶手機號併成功返回。
返回格式與微信官方網站上一致。
格式以下
`{ "phoneNumber": "13580006666", "purePhoneNumber": "13580006666", "countryCode": "86", "watermark": { "appid":"APPID", "timestamp": TIMESTAMP } } ` * *
此處貼上實現代碼(後端接收由SpringBoot實現):
Controller層實現:
`@PostMapping("/member/phone") @ApiOperation(value = "獲取用戶手機號", notes = "說明:") public PlatformResult getPhoneNumber( @ApiParam(name = "encryptedData", value = "加密數據") @RequestParam("encryptedData") String encryptedData, @ApiParam(name = "iv", value = "加密算法的初始向量") @RequestParam("iv") String iv) throws Exception { //TODO:從redis裏獲取sessionKey String sessionKey = (String) redisUtil.get(sessionKeyPre + jwtTokenInfoUtil.getMemberIdByToken()); if (null != sessionKey && !sessionKey.isEmpty()) { String s = AesUtil.wxDecrypt(encryptedData, sessionKey, iv); JSONObject object = JSONObject.parseObject(s); Object number = object.get("phoneNumber"); if (null != number) { MeMemberInfo memberInfo = new MeMemberInfo(); memberInfo.setId(jwtTokenInfoUtil.getMemberIdByToken()); memberInfo.setPhone(number.toString());//更新綁定手機號 memberInfo.setPhoneStatus(ComConstants.PHONE_STATUS_0);//置爲綁定手機狀態 readMeMemberInfoService.updateById(memberInfo); return PlatformResult.success(number.toString()); } } return PlatformResult.failure(ResultCode.RESULE_DATA_NONE); }` * *
解密工具AesUtil實現:
package com.andrea.platform.util; import java.security.spec.AlgorithmParameterSpec; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.apache.commons.codec.binary.Base64; /** * @program: aes-service * @description: 加密信息解密 * @author: Andrea_nul * @create: 2020-9-28 23:57 **/ public class AesUtil { public static String wxDecrypt (String encrypted, String sessionKey, String iv)throws Exception { byte[] encrypData = Base64.decodeBase64(encrypted); byte[] ivData = Base64.decodeBase64(iv); byte[] sKey = Base64.decodeBase64(sessionKey); String decrypt = decrypt(sKey,ivData,encrypData); return decrypt; } public static String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception { // AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv); // Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); // SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); // cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); //解析解密後的字符串 String resultString = null; AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv); SecretKeySpec keySpec = new SecretKeySpec(key, "AES"); try { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); resultString = new String(cipher.doFinal(encData), "UTF-8"); } catch (Exception e) { Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec); resultString = new String(cipher.doFinal(encData), "UTF-8"); } return resultString; } }
好了,完美解決,哎,真的是,可是我以爲不是我實力的問題,確定是微信官網沒有寫好(就這樣,不容反駁,嘿嘿嘿)
好了,最後仍是建議你們,沒啥事的時候能夠多去看一下官網的內容,或者本身debug一下源碼,總能發現一些在平時開發的時候遇不到的問題,解決的問題的過程就是成長
我是小Q,熱愛技術,也愛生活,堅持分享輸出,讓本身和讀者都有收穫!關注我來跟我一塊兒變強吧。公衆號:Java架構師聯盟,每日更新技術好文