记一次微信小程序+springboot获取用户手机号码

参考了一份demo 但是不知道出处。很尴尬。

一、首先使用mpvue+vant搭建一个简单的前端

<template>
    <van-dialog
      title="获取手机号码"
      message="牛儿洗护订单详情要求获取您的手机号码"
      :show="isLoginShow"
      show-cancel-button
      confirm-button-open-type="getPhoneNumber"
      @getphonenumber="getPhoneNumber"
      @confirm="onConfirm"
      @cancel="onCancel"
    >
    </van-dialog>
</template>

注:这里通过getphoneNumber方法去实现具体逻辑。

  confirm-button-open-type="getPhoneNumber"

注意一定要将这个属性配置为getPhoneNumber否则无法获取解密所需的参数

二、实现getphoneNumber方法

      getPhoneNumber (e) {
        // 将this赋给_this已供全局使用
        const _this = this;
        // console.log(_this.code)
        // console.log(e.mp.detail.iv)
        // console.log(e.mp.detail.encryptedData)
       	//  需要先执行微信小程序原生的登录方法去获取 code。
        wx.login({
          success(res) {
            // console.log(res);
            _this.code = res.code
            console.log(_this.code);
            // 将code、encryptedData和iv作为参数交给SpringBoot去做解析
            wx.request({
              url:URL+'/api/add_phone',
              method: 'get',
              data:{
                "code":_this.code,
                "encryptedData":e.mp.detail.encryptedData,//注意 mpvue里面detail数据封装再e.mp下面,在原生的基础上又封装了一层。
                "iv":e.mp.detail.iv
              },
              header: {'Content-Type': 'application/json'},
              success: function(res) {
                // 在控制台打印解析好的数据
                console.log(res)
              },
              fail:function (res) {
                // console.log(res);
              }
            })
          }
        })

      },

三、后端实现数据解码 获取出用户手机号码

这里具体的springboot工程搭建我就不再列出来了 嘻嘻

1)准备工作

工程需要用到的maven依赖

<!-- 谷歌官方退出的Gson  json解析工具  -->
<dependency>
    <groupId>com.google.code.gson</groupId>
    <artifactId>gson</artifactId>
    <version>2.8.2</version>
</dependency>
<!--  解码器-->
<dependency>
    <groupId>org.bouncycastle</groupId>
    <artifactId>bcprov-jdk16</artifactId>
    <version>1.46</version>
</dependency>
<!-- 发起http请求所需要的包 -->
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpcore</artifactId>
    <version>4.4.10</version>
</dependency>
<dependency>
    <groupId>org.apache.httpcomponents</groupId>
    <artifactId>httpclient</artifactId>
    <version>4.5.6</version>
</dependency>
2)调用封装的解码工具类做解码
    @GetMapping(value = "add_phone")
    public ApiResponse addPhone(
            @RequestParam(name = "code",required = true)
                    String code,
            @RequestParam(name = "encryptedData",required = true)
                    String encryptedData,
            @RequestParam(name = "iv",required = true)
                    String iv
    ) {
        //从code中获取openId 和sessionKey。sessionKey是解码所需要的一个参数
        OpenInfo openInfo = WechatDecryptDataUtil.getOpenId(code);
        // 初始化json解析工具
        JsonParser jsonParser = new JsonParser();
        // openInfo不能为空 没有sessionKey 不能做解码
        assert openInfo != null;
        String result = WechatDecryptDataUtil.decryptData(
                encryptedData,
                openInfo.getSessionKey(),
                iv
        );
        // 调用JsonObject解析返回的json数据
        JsonObject obj = (JsonObject) jsonParser.parse(result);
        // 赋值
        MiniProUser miniProUser = new MiniProUser();
        miniProUser.setOpenId(openInfo.getOpenId());
        miniProUser.setCountryCode(obj.get("countryCode").toString().replace("\"", "").trim());
        miniProUser.setPhoneNumber(obj.get("phoneNumber").toString().replace("\"", "").trim());
        miniProUser.setTimestamp(String.valueOf(System.currentTimeMillis()));
        // miniProUserService.addParkingUser(miniProUser);
        // 这里的ApiResponse是一个Restful的封装 大家可以用自己定义的就好。
        return  ApiResponse.ofSuccess(200,"响应成功",miniProUser);
    }
3)解析工具类

/**
 * 微信工具类
 */
public class WechatDecryptDataUtil {
    public static OpenInfo getOpenId(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Constant.APP_ID +
                "&secret=" + Constant.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";

        HttpUtil httpUtil = new HttpUtil();
        try {
            // 以GET方式发起网络请求
            HttpResult httpResult = httpUtil.doGet(url, null, null);
            if(httpResult.getStatusCode() == 200) {
                JsonParser jsonParser = new JsonParser();
                JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody());
                if(obj.get("errcode") != null) {
                    return null;
                } else {
                    OpenInfo openInfo = new OpenInfo();
                    openInfo.setOpenId(obj.get("openid").toString().replace("\"", "").trim());
                    openInfo.setSessionKey(obj.get("session_key").toString().replace("\"", "").trim());
                    return openInfo;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
    public static String getSession(String code) {
        String url = "https://api.weixin.qq.com/sns/jscode2session?appid=" + Constant.APP_ID +
                "&secret=" + Constant.APP_SECRET + "&js_code=" + code + "&grant_type=authorization_code";
        HttpUtil httpUtil = new HttpUtil();
        try {
            HttpResult httpResult = httpUtil.doGet(url, null, null);
            if(httpResult.getStatusCode() == 200) {

                JsonParser jsonParser = new JsonParser();
//                System.out.println(httpResult.getBody());
                JsonObject obj = (JsonObject) jsonParser.parse(httpResult.getBody());

//                log.error("getOpenId: " + obj.toString());

                if(obj.get("errcode") != null) {
//                    log.error("getOpenId returns errcode: " + obj.get("errcode"));
                    return "";
                } else {
                    return obj.get("session_key").toString();
                }
                //return httpResult.getBody();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "";
    }
    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;
    }

}

4)附件
I MiniProUser封装
/**
 * @author lyred
 */
@Data
@TableName(value = "mini_pro_user")
public class MiniProUser {
    @TableId(type = IdType.AUTO)
    private int id;
    @TableField(value = "phone_number")
    private String phoneNumber;
    @TableField(value = "open_id")
    private String openId;
    @TableField(value = "country_code")
    private String countryCode;
    @TableField(value = "timestamp")
    private String timestamp;
}
II OpenInfo 封装
@Data
public class OpenInfo {
    private String openId;
    private String sessionKey;
}

III 常量封装

public class Constant {

    public static final String APP_ID = "appid";

    public static final String APP_SECRET = "app_secret";

    public static final String APP_KEY = "app_key";
}

IV 发起网络请求用到的工具类
public class HttpUtil {

	// User-Agent
	public static final String USERAGENT_FIREFOX = "Mozilla/5.0 (Windows NT 6.3; WOW64; rv:39.0) Gecko/20100101 Firefox/39.0";
	public static final String USERAGENT_IE = "Mozilla/5.0 (Windows NT 6.3; WOW64; Trident/7.0; rv:11.0) like Gecko";

	private CloseableHttpClient httpClient;

	private BasicCookieStore cookieStore;
	private HttpGet get;
	private HttpPost post;

    public static StringBuffer httpsRequest(String requestUrl, String requestMethod, String output) throws IOException {
        URL url = new URL(requestUrl);
        HttpsURLConnection connection = (HttpsURLConnection) url.openConnection();
        connection.setDoOutput(true);
        connection.setDoInput(true);
        connection.setUseCaches(false);
        connection.setRequestMethod(requestMethod);
        if (null != output) {
            OutputStream outputStream = connection.getOutputStream();
            outputStream.write(output.getBytes("UTF-8"));
            outputStream.close();
        }
        // 从输入流读取返回内容
        InputStream inputStream = connection.getInputStream();
        InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
        BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
        String str = null;
        StringBuffer buffer = new StringBuffer();
        while ((str = bufferedReader.readLine()) != null) {
            buffer.append(str);
        }
        bufferedReader.close();
        inputStreamReader.close();
        inputStream.close();
        inputStream = null;
        connection.disconnect();
        return buffer;
    }


	public HttpResult doGet(String url, Map<String, String> headers, Map<String, String> params) throws NoSuchAlgorithmException, KeyStoreException, KeyManagementException, ClientProtocolException, IOException {

		if (url == null|| url.equals("")) {
			return null;
		}

		SSLContextBuilder builder = new SSLContextBuilder();
		builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
		cookieStore = new BasicCookieStore();
		CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore)
				.setSSLSocketFactory(sslsf).build();

		HttpResult result = null;
		try {

			url = url + "?" + parseParams(params);
			HttpGet httpget = new HttpGet(url);
			httpget.setHeaders(parseHeader(headers));

			CloseableHttpResponse response = httpclient.execute(httpget);
			try {
				HttpEntity entity = response.getEntity();

				if (entity != null) {
					result = new HttpResult();
					result.setCookies(cookieStore.getCookies());
					result.setStatusCode(response.getStatusLine().getStatusCode());
					result.setHeaders(response.getAllHeaders());
					result.setBody(EntityUtils.toString(entity));
				}

			} finally {
				response.close();
			}
		} finally {
			httpclient.close();
		}

		return result;

	}

	public HttpResult doPost(String url, Map<String, String> headers, Map<String, String> postData, String encoding) throws Exception {
		if (url == null|| url.equals("")) {
			return null;
		}
		if (encoding == null|| encoding.equals("")) {
			encoding = "utf-8";
		}
		SSLContextBuilder builder = new SSLContextBuilder();
		builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
		cookieStore = new BasicCookieStore();
		CloseableHttpClient httpClient = HttpClients.custom().setDefaultCookieStore(cookieStore)
				.setSSLSocketFactory(sslsf).build();
		post = new HttpPost(url);
		List<NameValuePair> list = new ArrayList<NameValuePair>();
		for (String tmp : postData.keySet()) {
			list.add(new BasicNameValuePair(tmp, postData.get(tmp)));
		}
		post.setEntity(new UrlEncodedFormEntity(list, encoding));
		post.setHeaders(parseHeader(headers));
		CloseableHttpResponse response = httpClient.execute(post);
		HttpEntity entity = response.getEntity();
		HttpResult result = new HttpResult();
		result.setCookies(cookieStore.getCookies());
		result.setStatusCode(response.getStatusLine().getStatusCode());
		result.setHeaders(response.getAllHeaders());
		result.setBody(EntityUtils.toString(entity, encoding));
		close(entity, response);
		return result;
	}
	private String parseParams(Map<String, String> params) {
		if (params == null || params.isEmpty()) {
			return "";
		}

		StringBuffer sb = new StringBuffer();
		for (String key : params.keySet()) {
			sb.append(key + "=" + params.get(key) + "&");
		}
		return sb.substring(0, sb.length() - 1);
	}
	private Header[] parseHeader(Map<String, String> headers) {
		if (headers == null || headers.isEmpty()) {
			return getDefaultHeaders();
		}
		Header[] retHeader = new BasicHeader[headers.size()];
		int i = 0;
		for (String str : headers.keySet()) {
			retHeader[i++] = new BasicHeader(str, headers.get(str));
		}
		return retHeader;
	}
	private Header[] getDefaultHeaders() {
		Header[] headers = new BasicHeader[3];
		headers[0] = new BasicHeader("User-Agent", USERAGENT_IE);
		headers[1] = new BasicHeader("Accept-Encoding", "gzip, deflate");
		headers[2] = new BasicHeader("Accept-Language", "en-US,en;q=0.8,zh-Hans-CN;q=0.5,zh-Hans;q=0.3");
		return headers;
	}
	private void close(HttpEntity entity, CloseableHttpResponse response) {
		try {
			if (entity != null) {
				InputStream input = entity.getContent();
				input.close();
			}
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				response.close();
			} catch (IOException e) {
				//e.printStackTrace();
			}
		}
	}
	/**
	 * 下载文件
	 * @param url 下载文件的链接
	 * @param destFile 包含路径的目标文件名
	 * @param headers 请求头
	 * @return
	 */
	public HttpResult downloadFile(String url, String destFile, Map<String, String> headers) throws Exception {
		SSLContextBuilder builder = new SSLContextBuilder();
		builder.loadTrustMaterial(null, new TrustSelfSignedStrategy());
		SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(builder.build());
		BasicCookieStore cookieStore = new BasicCookieStore();
		CloseableHttpClient httpclient = HttpClients.custom().setDefaultCookieStore(cookieStore).setSSLSocketFactory(sslsf).build();
		HttpGet get = new HttpGet(url);
		get.setHeaders(parseHeader(headers));
		InputStream input = null;
		CloseableHttpResponse response = null;
		HttpResult result = null;
		try {
			response = httpclient.execute(get);
			HttpEntity entity = response.getEntity();
			input = entity.getContent();
			File file = new File(destFile);

			FileOutputStream fos = new FileOutputStream(file);
			int len = -1;
			byte[] tmp = new byte[1024];
			while((len=input.read(tmp)) != -1) {
				fos.write(tmp, 0, len);
			}
			fos.flush();
			fos.close();
			result = new HttpResult();
			result.setCookies(cookieStore.getCookies());
			result.setStatusCode(response.getStatusLine().getStatusCode());
			result.setHeaders(response.getAllHeaders());
			result.setBody(EntityUtils.toString(entity, Consts.UTF_8));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				if(input != null) {
					input.close();
				}
				if(response != null) {
					response.close();
				}
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
		return result;
	}
}
V 网络请求结果的分装
public class HttpResult {

	private List<Cookie> cookies;

    private HashMap<String, Header> headers;

    private int statusCode;

    private String body;

	public List<Cookie> getCookies() {
		return cookies;
	}
	public void setCookies(List<Cookie> cookies) {
		this.cookies = cookies;
	}
	public HashMap<String, Header> getHeaders() {
		return headers;
	}
	public void setHeaders(Header[] headerAll) {
		headers = new HashMap<String, Header>();
        for (Header header : headerAll) {
        	headers.put(header.getName(), header);
        }
	}
	public int getStatusCode() {
		return statusCode;
	}
	public void setStatusCode(int statusCode) {
		this.statusCode = statusCode;
	}
	public String getBody() {
		return body;
	}

	public void setBody(String body) {
		this.body = body;
	}
	@Override
    public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("======================= HttpResult toString start ========================\n");
		sb.append("----- statusCode: " + statusCode + "\n");
    	if(headers != null) {
    		sb.append("----- headers:\n");
    		for(String key : headers.keySet()) {
    			sb.append("\t" + key + " : " + headers.get(key) + "\n");
    		}
    	}
    	if(cookies != null) {
    		sb.append("----- cookies:\n");
    		for(Cookie cookie : cookies) {
    			sb.append("\t" + cookie.getName() + " : " + cookie.getValue() + "\n");
    		}
    	}
    	sb.append("======================= body start ========================\n");
		sb.append(body);
    	sb.append("======================= body end ========================\n");
    	sb.append("======================= HttpResult toString end   =======================");
    	return sb.toString();
    }
	public String getCookieValue(String cookieName) {
		if(cookies.isEmpty()) {
			return null;
		}
		for(Cookie cookie : cookies) {
			if(cookie.getName().equals(cookieName)) {
				return cookie.getValue();
			}
		}
		return null;
	}
}