前言
公司最近开发一个程序,需求是要通过用户手机号去数据库获得CUSTOMERID,根据CUSTOMERID来进行下面的一系列操作。现总结一下经验:
1.授权获取密文
根据微信小程序官方文档的说明,要获取手机号加密密文必须通过button组件的bindgetphonenumber事件回调。因此根据需求不同可以有三种方式授权:
①.首页设为授权页
用户第一次进入小程序先让用户授权,不然无法跳转其他页面,取得CUSTOMERID后存入本地用户下次登录小程序无需再次授权。
②在登录页获取授权
在登录页设置两个button组件,后台JS的data里面添加布尔变量isLogin,默认为false,后台获得CUSTOMERID后在成功回调函数里面把isLogin设为true,这样两个button组件在授权前后就只会出现一个。
<button wx:if="{{isLogin}}" class="next-step" bindtap="login">下一步</button>
<button wx:else class="next-step" open-type="getPhoneNumber" bindgetphonenumber="getPhoneNumber">授权</button>
③底部导航栏“我的”
在该页设置登录按钮,这也是大多数小程序采用的方法。
点击授权按钮并且同意之后会获得一大串的加密密文encryptedData和加密算法的初始向量iv如下:
2.获取密钥
利用小程序提供的API中的wx.login方法来获取临时登录凭证 code,然后通过访问微信提供的接口地址来获取密钥session_key。
https://api.weixin.qq.com/sns/jscode2sessionappid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
PS:APPID和SECRET的获取链接,登录之后可以查看。
在相应位置填入参数,发送GET请求,成功后会返回session_key。代码如下:
wx.login({
success: function (res) {
wx.request({
url: 'https://api.weixin.qq.com/sns/jscode2session?appid=wx9*****ff540c64e&secret=3fd147f9e24******738bb84fae30be&js_code=' + res.code + '&grant_type=authorization_code',
success: function (res) {
wx.setStorageSync('key', res.data.session_key)
}
})
}
})
PS :并不推荐这样写,官方文档也说了不要把session_key传到前端,应该在自己的服务器中请求API地址,完成解密后返回手机号信息即可。
3.解密
解密需要一个依赖类,Maven如下:
<dependency> <groupId>org.bouncycastle</groupId> <artifactId>bcprov-jdk16</artifactId> <version>1.46</version> </dependency>
把目前获取到的密文,密钥,初始向量传入后台。
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.security.Key;
import java.security.Security;
import java.util.Arrays;
import static org.bouncycastle.util.encoders.Base64.decode;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
@RestController
@RequestMapping("/user")
public class UserController {
private static final String KEY_ALGORITHM = "AES";
private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding";
private static Key key;
private static Cipher cipher;
@RequestMapping("getPhoneNumber")
public String getPhoneNumber(String encryptedData,String key,String iv) {
String data = decryptData(encryptedData, key, iv);
JSONObject jsonObject = JSON.parseObject(data);
String phoneNumber = jsonObject.get("phoneNumber").toString();
return phoneNumber;
}
public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) {
return new String(
decryptOfDiyIV(
decode(encryptDataB64),
decode(sessionKeyB64),
decode(ivB64)
)
);
}
/**
* 解密方法
*
* @param encryptedData 要解密的字符串
* @param keyBytes 解密密钥
* @param ivs 自定义对称解密算法初始向量 iv
* @return 解密后的字节数组
*/
private static byte[] decryptOfDiyIV(byte[] encryptedData, byte[] keyBytes, byte[] ivs) {
byte[] encryptedText = null;
init(keyBytes);
try {
cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(ivs));
encryptedText = cipher.doFinal(encryptedData);
} catch (Exception e) {
e.printStackTrace();
}
return encryptedText;
}
}
private static void init(byte[] keyBytes) {
// 如果密钥不足16位,那么就补足. 这个if 中的内容很重要
int base = 16;
if (keyBytes.length % base != 0) {
int groups = keyBytes.length / base + (keyBytes.length % base != 0 ? 1 : 0);
byte[] temp = new byte[groups * base];
Arrays.fill(temp, (byte) 0);
System.arraycopy(keyBytes, 0, temp, 0, keyBytes.length);
keyBytes = temp;
}
// 初始化
Security.addProvider(new BouncyCastleProvider());
// 转化成JAVA的密钥格式
key = new SecretKeySpec(keyBytes, KEY_ALGORITHM);
try {
// 初始化cipher
cipher = Cipher.getInstance(ALGORITHM_STR, "BC");
} catch (Exception e) {
e.printStackTrace();
}
}
PS:代码参考作者@名字叫孙冉
注意:根据上面的方法获得的解密data数据是json格式字符串,需要再转为JSON对象才能取到里面的phoneNumer
结果如下: