1.小程序端登录后,得到登录时获取的 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三个参数,进行解密操作
基于jdk1.8,低于1.8版本需要创建一个base64解密工具类
(1)微信手机号信息解密后的对象
需要注意的是,WeixinWaterMark水印对象需要通过JSON字符串转化获得
import com.alibaba.fastjson.JSON;
/**
* @description: 微信手机号信息解密后的对象
* @author: wrt
*/
public class WeixinPhoneDecryptInfo {
private String phoneNumber;
private String purePhoneNumber;
private int countryCode;
private String watermark;
private WeixinWaterMark weixinWaterMark;
public String getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(String phoneNumber) {
this.phoneNumber = phoneNumber;
}
public String getPurePhoneNumber() {
return purePhoneNumber;
}
public void setPurePhoneNumber(String purePhoneNumber) {
this.purePhoneNumber = purePhoneNumber;
}
public int getCountryCode() {
return countryCode;
}
public void setCountryCode(int countryCode) {
this.countryCode = countryCode;
}
public String getWatermark() {
return watermark;
}
public void setWatermark(String watermark) {
this.watermark = watermark;
this.weixinWaterMark = JSON.toJavaObject(JSON.parseObject(this.watermark),WeixinWaterMark.class);
}
public WeixinWaterMark getWeixinWaterMark(){
return weixinWaterMark;
}
@Override
public String toString() {
return "WeixinPhoneDecryptInfo{" +
"phoneNumber='" + phoneNumber + '\'' +
", purePhoneNumber='" + purePhoneNumber + '\'' +
", countryCode=" + countryCode +
", appid=" + weixinWaterMark.getAppid() +
", timestamp=" + weixinWaterMark.getTimestamp() +
'}';
}
}
(2)水印对象
时间戳做转换的时候,记得先乘以1000,再通过simpledateformat完成date类型转换
public class WeixinWaterMark {
private Long timestamp;
private String appid;
public Long getTimestamp() {
return timestamp;
}
public void setTimestamp(Long timestamp) {
this.timestamp = timestamp;
}
public String getAppid() {
return appid;
}
public void setAppid(String appid) {
this.appid = appid;
}
}
这一部分的代码,参考链接博客链接书写,感谢博主!
实际使用中,由于特殊字符存在,可能导致base64解密失败,推荐引入org.apache.xmlbeans.impl.util.Base64,而不是java.util.Base64,感谢Prometheus_K反馈!
import java.nio.charset.StandardCharsets;
import java.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 com.alibaba.fastjson.JSON;
import entity.WeixinPhoneDecryptInfo;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
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");
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());
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;
}
}
用于JSON数据处理的fastjson
用于解密所需的bouncycastle
<dependency>
<groupId>com.alibabagroupId>
<artifactId>fastjsonartifactId>
<version>1.2.61version>
dependency>
<dependency>
<groupId>org.bouncycastlegroupId>
<artifactId>bcprov-jdk16artifactId>
<version>1.46version>
dependency>
public class AEStest {
public static void main(String[] args) {
String encryptedData="加密的敏感数据";
String iv="初始向量";
String appId="小程序id";
AESForWeixinGetPhoneNumber aes=new AESForWeixinGetPhoneNumber(encryptedData,sessionKey,iv);
WeixinPhoneDecryptInfo info=aes.decrypt();
if (null==info){
System.out.println("error");
}else {
if (!info.getWeixinWaterMark().getAppid().equals(appId)){
System.out.println("wrong appId");
}
System.out.println(info.toString());
}
}
}