1.授权
授权是有前端完成的,授权后,前端可以获取到code,后台根据code获取微信小程序用户对应的openid和session_key,并根据openid判断是否是新用户,代码如下:
@Value("${app.weixin.mn.appid}") String appidxcx; @Value("${app.weixin.mn.secret}") String secretxcx; @ApiOperation(value = "微信小程序校验用户是否存在", httpMethod = "POST", produces = "application/json;charset=UTF-8") @ApiImplicitParam(value = "code", name = "code",defaultValue ="", dataType = "String",paramType="query") @PostMapping("/api/user/checkUserXcx") public Result checkUserXcx(String code){ Map<String, Object> data = new HashMap<>(); try { String url="https://api.weixin.qq.com/sns/jscode2session?appid="+appidxcx+ "&secret="+secretxcx+"&js_code="+ code +"&grant_type=authorization_code"; JSONObject jsonObject = AuthUtil.doGetJson(url); if(jsonObject.has("errcode")){ Integer errcode = jsonObject.getInt("errcode"); if(null!=errcode){ String errmsg = jsonObject.getString("errmsg"); return Result.fail(errcode,errmsg); } } String openid = jsonObject.getString("openid"); String session_key = jsonObject.getString("session_key"); User user = userService.getDao().getRepo().findByOpenidxcx(openid); data.put("user",user); data.put("openid",openid); data.put("session_key",session_key); }catch (Exception e){ e.printStackTrace(); return Result.error(901,"微信小程序校验用户失败!"); } return Result.ok(data); } |
2.到此,前端已经获取到用户信息了,但是获取到的用户手机号是加密的,前端需要传参数到后台对手机号进行解密,代码如下:
@ApiOperation(value = "解密手机号", httpMethod = "POST", produces = "application/json;charset=UTF-8") @ApiImplicitParams(value = {@ApiImplicitParam(value = "code", name = "sessionKey",defaultValue ="", dataType = "String",paramType="query"), @ApiImplicitParam(value = "encryptedData", name = "encryptedData",defaultValue ="", dataType = "String",paramType="query"), @ApiImplicitParam(value = "iv", name = "iv",defaultValue ="", dataType = "String",paramType="query")}) @PostMapping("/api/user/getUserPhone") public Result getUserPhone(String sessionKey,String encryptedData,String iv){ Map<String, Object> data = new HashMap<>(); try { String phone = WechatDecryptDataUtil.decryptData(encryptedData, sessionKey, iv); data.put("datas",phone); }catch (Exception e){ e.printStackTrace(); return Result.error(901,"解密手机号失败!"); } return Result.ok(data); } |
上述代码中用到的工具类:
WechatDecryptDataUtil.class
import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.bouncycastle.util.Arrays; import org.bouncycastle.util.encoders.Base64; import javax.crypto.Cipher; import javax.crypto.spec.IvParameterSpec; import javax.crypto.spec.SecretKeySpec; import java.security.Key; import java.security.Security; /** * 微信工具类 */ public class WechatDecryptDataUtil { public static void main(String[] args) { String result = decryptData( "CpTbp86N6BtlEDq6r83bGpkGzxvkdaFAWNcKXVQcjY5Ekd+gfAuRC4fwndUEBjbD8WCpuIaR9l6aSUugIJTVdZ6rL8DCZciKS2puvbNYM6zYth2RYEi6YmzRScWINEFIzPBrfP7t/e5ADn7sGMnmGHAZ+6puccgAfjTQnzoS94JqFaF+EHsQCP7IDUpH1zaFiLwdojxDUu3FSfVAV4MYFA==", "9y9P6RqKVZiWg==", "Yi1SGcsMg==" ); System.out.println("result = " + result); } public static String decryptData(String encryptDataB64, String sessionKeyB64, String ivB64) { return new String( decryptOfDiyIV( Base64.decode(encryptDataB64), Base64.decode(sessionKeyB64), Base64.decode(ivB64) ) ); } private static final String KEY_ALGORITHM = "AES"; private static final String ALGORITHM_STR = "AES/CBC/PKCS7Padding"; private static Key key; private static Cipher cipher; 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(); } } /** * 解密方法 * * @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; } } |
整体思路:
1.前端通过授权获取到code传到后台,后台根据code获取openid和session_key返回给前端,此时前端已获取到用户所有信息
2.用户的手机号是加密的,需要解密,前端传encryptedData、sessionKey和iv到后台,后台对手机号进行解密并返回给前端,结束。