微信官方文档: https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html.
废话不多说,直接上代码
WeChatPhoneDTO
@Data
public class WeChatPhoneDTO {
//包括敏感数据在内的完整用户信息的加密数据
private String encryptedData;
//加密算法的初始向量
private String iv;
//会话密钥
private String sessionKey;
//用户openId
private String openId;
}
Controller
@GetMapping("/phone")
public ResponseResult getPhone(WeChatPhoneDTO weChatPhoneDTO) {
ResponseResult phone = insuranceWeChatService.getPhone(weChatPhoneDTO);
return phone;
}
Serviceimpl
@Override
public ResponseResult getPhone(WeChatPhoneDTO weChatPhoneDTO) {
AESForWeixinGetPhoneNumber aes=new AESForWeixinGetPhoneNumber(weChatPhoneDTO.getEncryptedData(),weChatPhoneDTO.getSessionKey(),weChatPhoneDTO.getIv());
WeixinPhoneDecryptInfo info = aes.decrypt();
if (info != null){
if (!info.getWeixinWaterMark().getAppid().equals(Constant.APP_ID)){
log.error("错误的appId{}",Constant.APP_ID);
}
//根据openId查询数据
InsuranceMemberDO dto = insuranceMemberDao.getListByOpenId(weChatPhoneDTO.getOpenId());
if (dto != null){
if (dto.getPhone() == null || !dto.getPhone().equals(info.getPhoneNumber())){
//如果数据库的电话和解码的电话不同,根据openId将手机号码保存进去
Map<String,Object> map = new HashMap<>();
map.put("phone",info.getPhoneNumber());
map.put("openId",weChatPhoneDTO.getOpenId());
insuranceMemberDao.updateByOpenId(map);
}
dto.setPhone(info.getPhoneNumber());
}
return new ResponseData<>(CommonCode.SUCCESS, dto);
}
return null;
}
WeixinPhoneDecryptInfo
/**
* @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() +
'}';
}
}
AESForWeixinGetPhoneNumber
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;
}
}