小程序获取手机号前提:需要有一个非个人的开发者账号,且完成该小程序的认证(不包含海外主体)。建议从现有的公众号平台直接关联小程序,这样就省得再花300元去微信认证一波。当然如果没有那么可以忽略我刚刚说的话。
1.调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器,在服务器端调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key。
public Map<String,Object> oauth2GetOpenid(String code) {
String GetPageAccessTokenUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=CODE&grant_type=authorization_code";
String requestUrl = GetPageAccessTokenUrl.replace("APPID", “xxxxx”).replace("SECRET", "xxxxxxx").replace("CODE", code);
HttpClient client = null;
Map<String,Object> result =new HashMap<String,Object>();
try {
client = HttpClientBuilder.create().build();
HttpGet httpget = new HttpGet(requestUrl);
ResponseHandler<String> responseHandler = new BasicResponseHandler();
String response = client.execute(httpget, responseHandler);
net.sf.json.JSONObject OpenidJSONO=net.sf.json.JSONObject.fromObject(response);
String openid =String.valueOf(OpenidJSONO.get("openid"));
String session_key=String.valueOf(OpenidJSONO.get("session_key"));
String unionid=String.valueOf(OpenidJSONO.get("unionid"));
String errcode=String.valueOf(OpenidJSONO.get("errcode"));
String errmsg=String.valueOf(OpenidJSONO.get("errmsg"));
result.put("openid", openid);
result.put("sessionKey", session_key);
result.put("unionid", unionid);
result.put("errcode", errcode);
result.put("errmsg", errmsg);
} catch (Exception e) {
e.printStackTrace();
} finally {
client.getConnectionManager().shutdown();
}
return result;
}
2.根据会话id和敏感数据密文以及偏移量解密数据
public JSONObject decryptData(String sessionKey,String encryptedData, String iv) throws Exception{
byte[] dataByte = Base64.decodeBase64(encryptedData);
// 加密秘钥
byte[] keyByte = Base64.decodeBase64(sessionKey);
// 偏移量
byte[] ivByte = Base64.decodeBase64(iv);
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/PKCS5Padding","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");
return JSONObject.parseObject(result);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
注意点
1.需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。
2.需要将 button 组件 open-type 的值设置为 getPhoneNumber,当用户点击并同意之后,可以通过 getphonenumber 事件回调获取到微信服务器返回的加密数据, 然后在第三方服务端结合 session_key 以及 app_id 进行解密获取手机号。
<button open-type="getPhoneNumber" @getphonenumber="getPhoneNumber"></button>
调试时出现的问题
1.pad block corrupted ==》 我这边出现这个问题是因为解密和加密时的秘钥不一致,解决方式为:在调用wx.login()方法时,先用wx.checkSession()方法判断秘钥是否过期,若过期则重新请求,否则继续用之前的秘钥
参考
java版微信小程序AES加解密敏感数据获取手机号
获取手机号微信官方文档
若有不对的地方,欢迎各位大佬指正。