微信小程序登录接口——用户数据解密

1. 用户授权登录

1)appId 和 appSecret 获取方式
  • 进入微信公众平台网站 : https://mp.weixin.qq.com/
  • 如下图获取
  • 微信小程序登录接口——用户数据解密_第1张图片
2)登录过程,前端发送一个code,后端收到code后,访问微信接口获取openId和sessionKey,返回给前端
public Object wxLogin(String code) {
    RestTemplate restTemplate = new RestTemplate();
    log.info("微信登录 code====" + code);
    String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appId + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code";
    String s = restTemplate.getForObject(url, String.class);
    WxUserInfo wxUserInfo = JSONUtil.toBean(s, WxUserInfo.class);
    return wxUserInfo;
}
3)前端收到openId和sessionKey后,请求微信接口,可以获取用户手机号等信息的加密数据,然后发给后端,后端进行解密,解密后将数据进行存储,并生成token返回前端,登录结束(生成token代码省略)

2.用户数据解密代码

通过小程序获取用户数据,数据未加密数据,需要拿用户的sesstionKey进行解密,解密代码如下

@GetMapping("deciphering")
public ResultDTO deciphering(@RequestParam String encrypdata,
                          @RequestParam String ivdata,
                          @RequestParam String sessionKey,
                          @RequestParam String openId) {
    System.out.println("进入手机号鉴权");
    byte[] encrypData = Base64.decode(encrypdata);
    byte[] ivData = Base64.decode(ivdata);
    byte[] sessionKeys = Base64.decode(sessionKey);
    String str="";
    try{
        str = decrypt(sessionKeys,ivData,encrypData);
    }catch(Exception e){
        e.printStackTrace();
        throw new RuntimeException("登录失败,请重试");
    }
    Map<String,Object> map = JSONUtil.toBean(str, Map.class);
    String phoneNumber = map.get("phoneNumber").toString();
    SysUser user = new SysUser();
    user.setOpenId(openId);
    user.setPhonenumber(phoneNumber);
    user.setUsername("微信用户_" + phoneNumber.substring(7,11));
    user.setNickname("微信用户_" + phoneNumber.substring(7,11));
    ResultDTO loginToken = loginService.doWxLogin(user);
    log.info("微信用户 --[{}]-- 已通过小程序登录",user.getUsername());
    return loginToken;
}
 private  String decrypt(byte[] key, byte[] iv, byte[] encData) throws Exception {
        // 如果密钥不足16位,那么就补足.  这个if 中的内容很重要
        int base = 16;
        if (key.length % base != 0) {
            int groups = key.length / base + (key.length % base != 0 ? 1 : 0);
            byte[] temp = new byte[groups * base];
            Arrays.fill(temp, (byte) 0);
            System.arraycopy(key, 0, temp, 0, key.length);
            key = temp;

        }

        AlgorithmParameterSpec ivSpec = new IvParameterSpec(iv);
        String resultStr = null;
        /**
         * 数据解密问题:Given final block not properly padded.
         * 偶发性异常:没登录时用AES/CBC/PKCS7Padding,登录后用AES/CBC/PKCS5Padding;
         */

        try {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            resultStr = new String(cipher.doFinal(encData), "UTF-8");
        } catch (Exception e) {
            Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
            SecretKeySpec keySpec = new SecretKeySpec(key, "AES");
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            resultStr = new String(cipher.doFinal(encData), "UTF-8");
        }
        //解析解密后的字符串
        return resultStr;
    }

3. 可能出现的问题

报错:
Exception:javax.crypto.BadPaddingException: Given final block not properly padded. Such issues can arise if a bad key is used during decryption.

偶发性异常:没登录时用AES/CBC/PKCS7Padding,登录后用AES/CBC/PKCS5Padding;

原因 : 微信小程序的sessionKey有一个过期时间5分钟;

解决方法:代码已在上面给出。

你可能感兴趣的:(Springboot,微信小程序,小程序)