小程序端登錄后,得到登錄時獲取的 code(僅可使用一次)
App({ onLaunch: function () { // 登錄 wx.login({ success: res => { // 發送 res.code 到后台換取 openId, sessionKey, unionId console.log(res.code) } }) } })
2.后端訪問微信登錄憑證校驗API
小程序官方文檔地址-登錄憑證.
通過官方提供的接口,獲取到會話密鑰session_key和openid
https://api.weixin.qq.com/sns/jscode2session?appid=‘小程序id’&secret='小程序AppSecret '&js_code=‘步驟1中得到的code’&grant_type=authorization_code
3.小程序請求客戶授權獲取手機號
小程序官方文檔地址-獲取手機號
(1)頁面上增加一個按鈕,用戶點擊后獲取到敏感數據
<button open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">獲取手機號</button>
(2)將e.detail傳到后端
Page({ getPhoneNumber (e) { console.log(e.detail.errMsg) console.log(e.detail.iv) console.log(e.detail.encryptedData) } })
4.結合sessionKey、encryptedData、iv三個參數,進行解密操作
二、Java后端解密數據
基於jdk1.8,低於1.8版本需要創建一個base64解密工具類
1、實體類
(1)微信手機號信息解密后的對象
public class WeixinPhoneDecryptInfo { private String phoneNumber; private String purePhoneNumber; private int countryCode; private String weixinWaterMark; private WaterMark watermark; }
public class WaterMark { private Long timestamp;// 時間戳做轉換的時候,記得先乘以1000,再通過simpledateformat完成date類型轉換 private String appid; ... }
2、解密工具類
這一部分的代碼,參考鏈接博客鏈接書寫,感謝博主!
實際使用中,由於特殊字符存在,可能導致base64解密失敗,推薦引入org.apache.xmlbeans.impl.util.Base64,而不是java.util.Base64,感謝Prometheus_K反饋!
import java.nio.charset.StandardCharsets; import java.security.AlgorithmParameters; import java.security.Key; import java.security.NoSuchAlgorithmException; import java.security.Security; import java.security.spec.InvalidParameterSpecException; import java.util.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import org.bouncycastle.jce.provider.BouncyCastleProvider; import com.alibaba.fastjson.JSON; public class AESForWeixinGetPhoneNumber { //加密方式 private static String keyAlgorithm = "AES"; //避免重復new生成多個BouncyCastleProvider對象,因為GC回收不了,會造成內存溢出 //只在第一次調用decrypt()方法時才new 對象 private static boolean initialized = false; //用於Base64解密 private Base64.Decoder decoder = Base64.getDecoder(); //待解密的數據 private String originalContent; //會話密鑰sessionKey private String encryptKey; //加密算法的初始向量 private String iv; public AESForWeixinGetPhoneNumber(String originalContent,String encryptKey,String iv) { this.originalContent = originalContent; this.encryptKey = encryptKey; this.iv = iv; } /** * AES解密 * 填充模式AES/CBC/PKCS7Padding * 解密模式128 * * @return 解密后的信息對象 */ public WeixinPhoneDecryptInfo decrypt() { initialize(); try { //數據填充方式 //Cipher cipher = Cipher.getInstance(“AES/CBC/PKCS7Padding”,”BC”); Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding"); Key sKeySpec = new SecretKeySpec(decoder.decode(this.encryptKey), keyAlgorithm); // 初始化 cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(decoder.decode(this.iv))); byte[]data = cipher.doFinal(decoder.decode(this.originalContent)); String datastr = new String(data, StandardCharsets.UTF_8); return JSON.toJavaObject(JSON.parseObject(datastr),WeixinPhoneDecryptInfo.class); } catch (Exception e) { System.out.println(e.getMessage()); System.out.println(222); return null; } } /**BouncyCastle作為安全提供,防止我們加密解密時候因為jdk內置的不支持改模式運行報錯。**/ private static void initialize() { if (initialized) { return; } Security.addProvider(new BouncyCastleProvider()); initialized = true; } // 生成iv private static AlgorithmParameters generateIV(byte[] iv) throws NoSuchAlgorithmException, InvalidParameterSpecException { AlgorithmParameters params = AlgorithmParameters.getInstance(keyAlgorithm); params.init(new IvParameterSpec(iv)); return params; } }
需要引入的jar包
<dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.61</version> </dependency> <dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version> </dependency>
測試
public Object a(String encryptedData,String iv,String sessionKey){ System.out.println("加密的敏感數據:" + encryptedData); System.out.println("初始向量:" + iv); System.out.println("會話密鑰:" + sessionKey); String appId = "XXXXXXXXX"; AESForWeixinGetPhoneNumber aes = new AESForWeixinGetPhoneNumber(encryptedData,sessionKey,iv); WeixinPhoneDecryptInfo info = aes.decrypt(); if (null==info){ System.out.println("error"); }else { System.out.println(info.toString()); if (!info.getWatermark().getAppid().equals(appId)){ System.out.println("wrong appId"); } } return info; }
WeixinPhoneDecryptInfo [phoneNumber=152XXXXXXXX, purePhoneNumber=152XXXXXXXX, countryCode=86, watermark={"appid":"XXXXXXXXXXXXX","timestamp":1600413039}, weixinWaterMark=null]
原文鏈接: https://blog.csdn.net/weixin_42792301/article/details/101540668