微信支付宝解析用户绑定的敏感数据(手机号码)

在我们公司的项目中,想要让用户在微信小程序和支付宝小程序中填写一些必须的信息资料,像用户的手机号码这些,但是对于一些用户可能会嫌麻烦,所以我们就想做到直接获取到微信绑定的手机号码和支付宝绑定的手机号码,只需要用户授权就可以了。
由于像微信、支付宝绑定手机号码为用户的敏感信息,前端只能够获取到用户信息的加密数据,需要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<String, String> openapiResult = JSON.parseObject(encryptContent,
                    new TypeReference<Map<String, String>>() {
                    }, Feature.OrderedField);
            String signType = StringUtils.defaultIfBlank(openapiResult.get("signType"), "RSA2");
            String charset = StringUtils.defaultIfBlank(openapiResult.get("charset"), "UTF-8");
            String encryptType = StringUtils.defaultIfBlank(openapiResult.get("encryptType"), "AES");
            String sign = openapiResult.get("sign");
            String content = openapiResult.get("response");

            boolean isDataEncrypted = !content.startsWith("{");
            boolean signCheckPass = false;

            //2. 验签
            String signContent = content;
            String signVeriKey = Ali.MINI_APP_PUBLIC_KEY;
            String decryptKey = Ali.AES;
            //如果是加密的报文则需要在密文的前后添加双引号
            if (isDataEncrypted) {
                signContent = "\"" + signContent + "\"";
            }
            try {
                signCheckPass = AlipaySignature.rsaCheck(signContent, sign, signVeriKey, charset, signType);
            } catch (AlipayApiException e) {
                //验签异常, 日志
                return ApiResult.error(ExceptionCode.ALI_SIGN_FAILURE, "验签异常");
            }
            if(!signCheckPass) {
                //验签不通过(异常或者报文被篡改),终止流程(不需要做解密)
                return ApiResult.error(ExceptionCode.ALI_SIGN_FAILURE, "验签不通过,异常或者报文被修改");
            }

            //3. 解密
            String plainData = null;
            if (isDataEncrypted) {
                try {
                    plainData = AlipayEncrypt.decryptContent(content, encryptType, decryptKey, charset);
                } catch (AlipayApiException e) {
                    //解密异常, 记录日志
                    return ApiResult.error(ExceptionCode.ALI_DECRIPT_FAILURE);
                }
            } else {
                plainData = content;
            }
            UserAliSecretInfoDTO userAliSecretInfoDTO = new Gson().fromJson(plainData, UserAliSecretInfoDTO.class);
            //System.out.println("获取到的解密信息为:" + new Gson().toJson(userAliSecretInfoDTO));
            return ApiResult.ok(userAliSecretInfoDTO);
        } catch (Exception e) {
            return ApiResult.error(ExceptionCode.ALI_DECRIPT_FAILURE, "验签解密异常");
        }
    }

其中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<String, String> openapiResult = JSON.parseObject(encryptContent,
new TypeReference<Map<String, String>>() {
}, Feature.OrderedField);里面的encryptContent就是传过来的一串东西,我一开始接收参数直接将它解析出来传进去的是解析出来的response,自然这样是解析不出来的。
3.支付宝小程序解析用户信息比较麻烦的,必须提交阿里那边审核说明为什么要获取用户敏感信息,审核通过后才能够获取,否则会报40006,即{"code":"40006","msg":"Insufficient Permissions","subCode":"isv.insufficient-isv-permissions","subMsg":"ISV权限不足,建议在开发者中心检查对应功能是否已经添加"}

支付宝小程序获取敏感数据我参考了以下网页以及官方文档:

https://docs.alipay.com/mini/introduce/aes
https://www.cnblogs.com/hujunzheng/p/10184418.html

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,558评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,002评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,036评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,024评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,144评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,255评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,295评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,068评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,478评论 1 305
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,789评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,965评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,649评论 4 336
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,267评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,982评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,800评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,847评论 2 351

推荐阅读更多精彩内容