在我们公司的项目中,想要让用户在微信小程序和支付宝小程序中填写一些必须的信息资料,像用户的手机号码这些,但是对于一些用户可能会嫌麻烦,所以我们就想做到直接获取到微信绑定的手机号码和支付宝绑定的手机号码,只需要用户授权就可以了。
由于像微信、支付宝绑定手机号码为用户的敏感信息,前端只能够获取到用户信息的加密数据,需要Java开发者后台进行解密,下面是根据微信和支付宝写的解析接口
微信:
导包如下(导入的包中包含了微信和支付宝接口的,还有可能会有一些无用包,因为我是直接把我写的接口拿出来了,这个控制器里面还有其他接口,就不粘了):
import java.security.AlgorithmParameters;
import java.security.Security;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import com.alibaba.fastjson.parser.Feature;
import com.alipay.api.AlipayApiException;
import com.alipay.api.internal.util.AlipayEncrypt;
import com.alipay.api.internal.util.AlipaySignature;
import com.google.gson.Gson;
import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;
import com.tp.dto.UserAliSecretInfoDTO;
import com.tp.dto.UserWxSecretInfoDTO;
import com.tp.search.UserWxSecretInfoSearch;
import org.apache.commons.lang3.StringUtils;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import org.springframework.web.bind.annotation.*;
import com.alibaba.fastjson.JSONObject;
import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipaySystemOauthTokenRequest;
import com.alipay.api.response.AlipaySystemOauthTokenResponse;
import com.squareup.okhttp.OkHttpClient;
import com.tp.ajax.ApiResult;
import com.tp.common.Constant.Ali;
import com.tp.common.Constant.Weixin;
import com.tp.enums.ExceptionCode;
import com.tp.exception.BaseException;
import com.tp.service.WxRemoteApiService;
import com.tp.utils.LogUtils;
import retrofit.Call;
import retrofit.GsonConverterFactory;
import retrofit.Retrofit;
import retrofit.RxJavaCallAdapterFactory;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
@ResponseBody
@PostMapping("/mini/wx/phone")
public ApiResult getMiniWxPhone(@RequestBody UserWxSecretInfoSearch userSecretInfoSearch) {
if (null == userSecretInfoSearch.getEncryptedData() || null == userSecretInfoSearch.getSessionKey() || null == userSecretInfoSearch.getIv()) {
throw new BaseException(ExceptionCode.PARAMETER_MISSING);
}
// 被加密的数据
byte[] dataByte = Base64.decode(userSecretInfoSearch.getEncryptedData());
// 加密秘钥
byte[] keyByte = Base64.decode(userSecretInfoSearch.getSessionKey());
// 偏移量
byte[] ivByte = Base64.decode(userSecretInfoSearch.getIv());
try {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyByte.length % base != 0) {
int groups = keyByte.length / base + (keyByte.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyByte, 0, temp, 0, keyByte.length);
keyByte = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding","BC");
SecretKeySpec spec = new SecretKeySpec(keyByte, "AES");
AlgorithmParameters parameters = AlgorithmParameters.getInstance("AES");
parameters.init(new IvParameterSpec(ivByte));
cipher.init(Cipher.DECRYPT_MODE, spec, parameters);// 初始化
byte[] resultByte = cipher.doFinal(dataByte);
if (null != resultByte && resultByte.length > 0) {
String result = new String(resultByte, "UTF-8");
UserWxSecretInfoDTO userSecretInfoDTO = new Gson().fromJson(result, UserWxSecretInfoDTO.class);
return ApiResult.ok(userSecretInfoDTO);
}
} catch (Exception e) {
e.printStackTrace();
}
return ApiResult.error(ExceptionCode.PARAMETER_WRONG,"反解密码失败");
}
其中UserWxSecretInfoSearch是一个我自己定义的接收类,我也贴出来:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
//BaseSearch类见我的中的工具类,另外:本篇文章中的像ApiResult和ExceptionCode都是自己定义的一些工具类和异常枚举类,里面是一些异常返回的code和一些具体的异常错误信息
@Data
@AllArgsConstructor
@NoArgsConstructor
@EqualsAndHashCode(callSuper = false)
public final class UserWxSecretInfoSearch extends BaseSearch {
//微信获取敏感信息手机号码
private String encryptedData;
private String sessionKey;
private String iv;
}
UserWxSecretInfoDTO也是我自己定义的一个接收类,如下:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserWxSecretInfoDTO {
String phoneNumber;
String purePhoneNumber;
String countryCode;
}
微信我是直接参考的下面的链接:
https://www.cnblogs.com/handsomejunhong/p/8670367.html
同时参考了微信的官方文档:
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/signature.html
https://developers.weixin.qq.com/miniprogram/dev/framework/open-ability/getPhoneNumber.html
支付宝获取用户的敏感数据
@ResponseBody
@PostMapping("/mini/ali/phone")
public ApiResult getMiniAliPhone(@RequestBody String encryptContent) {
//System.out.println("我调用了支付宝小程序的敏感信息的接口");
try {
//1. 获取验签和解密所需要的参数
Map openapiResult = JSON.parseObject(encryptContent,
new TypeReference
其中UserAliSecretInfoDTO是我自己定义的一个接收类,如下:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class UserAliSecretInfoDTO {
String code;
String msg;
String mobile;
}
这是我直接看官方文档然后百度借鉴了其他大神的获取敏感手机号的方法。
不过我在写的过程中遇到了几个坑:
1.首先是解析前端传过来的加密信息的时候,采用fastjson处理,但是要注意参数解析的顺序问题,即Feature.OrderedField,一开始一直没有OrderedField这个值,最后查看了是由于fastjson版本太老了,好像是1.2.0的,需要更换比较新的fastjson包,我换成了1.2.54版本的
2.传过来的也就是后台接收的参数encryptContent是包含了response、sign、sign_type、encrypt_type以及charset的字符串,Map
new TypeReference
支付宝小程序获取敏感数据我参考了以下网页以及官方文档:
https://docs.alipay.com/mini/introduce/aes
https://www.cnblogs.com/hujunzheng/p/10184418.html