【小程序】 获取unionid的方法 对称解密的 java代码 2018年5月16日

版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/u013408059/article/details/80342300

最近研究小程序 , 客户必须要unionid来对微信用户做免登陆的处理.

微信方还没提供解密的工具类,-_-||.

获取unionid需要满足的条件.

官方文档:


大致分两种方式:

1 如果用户用过该小程序开发者下的其他产品,比如公众号,或者其他应用,并且微信用户关注了公众号,或者给其他的授权过,通过
wx.login 返回的code就可以解析出unionid 点击查看微信官方说明



这个方式比较简单 , ,但是条件比较苛刻 , ,不过只要授权过,以后就会都用这个方法了 . 但是下面这种方式是[干净用户] 第一次必须走的方式:

2 通过解密数据:

如果获取解密数据就不啰嗦了,去看微信小程序官方API解释: 有两种方式可以获取.

emmmmmmmmmmmmmmm,,,,,,,,,,还是说明下吧,防止我忘了....

             方式1:用法点击看API


这个就比较火了,这个改动引发了微信社区的大暴动mmmmmmmmmm...

意思就是只要授权过才能用这个接口获取,如果没有授权直接调取这个接口及直接进fail函数.


              方式2:用法点击看API


写一个button, 必须带图中的两个属性 , 点击button 会触发"onGotUserInfo function(), 返回的 信息就包括我们需要的数据:

格式比较恶心: 他在返回数据中的detail对象的字段中.

--------------------------------------------------这是两种方式返回的格式-----------------------------------------------------


接下来就是获取unionid的流程了:

第一种方式简单

第二种需要去解密:

需要三个数据:

    

   session_key  这个是从解析 wx.login给的code 获取的 ,下面有代码
 encryptedData  这个不过多解释
iv 这个也是直接拿到的

    直接贴代码:

import java.util.HashMap;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.apache.commons.codec.binary.Base64;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import com.alibaba.fastjson.JSONObject;
import com.google.gson.Gson;
import com.jd.ecc.commons.web.model.RespData;
import com.jd.ecc.tianfu.config.SysConstants;
import com.jd.ecc.tianfu.service.WeChatService;
import com.jd.ecc.tianfu.tools.HttpClientUtils;
import com.jd.ecc.tianfu.utils.AES;

@Service
public class WeChatServiceImpl implements WeChatService {

	private Gson gson = new Gson();

	private static final Logger LOG = LoggerFactory.getLogger(WeChatServiceImpl.class);

	/**
	 * 1 先按照loginInfo信息查询有无有unionID
	 *  2 如果有,直接返回 ,程序结束 
	 *  3 如果没有,将用于解密的session_key获取
	 *  4 根据userInfo拿取encryptedData等加密数据
	 *  5 对加密数据进行逆解密 
	 *  6 拿到unionID 返回,程序结
	 * 
	 * userInfo TODO 注意一下:通过user.getInfo获取的和通过button授权获取的格式不一样
	 * 
	 */
	@Override
	public RespData getUnionID(Long platformId, String userInfo, String loginInfo, HttpServletRequest request) {

		Map<String, String> resLogign = null;
		JSONObject resUserInfo = null;

		LOG.info("--------------->>>>>进入WeChatServiceImpl");   
		resLogign = getUnionIDByLoginInfo(loginInfo, request); 
		    // 加密session_key 解密需要  
		String session_key = resLogign.get("session_key");
		if (StringUtils.isNotBlank(resLogign.get("unionid"))) {
			LOG.info("返回数据,获取unionid,程序结束<<<------unionid={}", resLogign.get("unionid"));
			return RespData.success("获取unionid成功,返回数据!", resLogign);
		} else {
			LOG.info("没有获取到unionid,userinfo进行对称解密操作start----->");

			resUserInfo = decipherByUserInfo(session_key, userInfo, request);
			if (resUserInfo != null && StringUtils.isNotEmpty(resUserInfo.toJSONString())) {
				LOG.info("解密后的信息为resUserInfo:{}", resUserInfo.toJSONString());
				// 解密信息由 data.get("unionid") 获取
				return RespData.success("获取解密信息成功", resUserInfo);
			}
		}
		return null;
	}

我特意拆成了两个方法: 

看方法名字就能看出来了:

/**
	 * 
	 * @param session_key
	 * @param userInfo
	 * @param request
	 * @return
	 */
	private  JSONObject decipherByUserInfo(String session_key, String userInfo, HttpServletRequest request) {

		LOG.info("--------------->>>>>进入 decipherByUserInfo");
		Map<String, String> resUserInfo = new HashMap<>();
		
		if (!userInfo.contains("login:ok")&&!userInfo.contains("getUserInfo:ok")) {
			return null;
		}
		JSONObject jsonUserInfo = JSONObject.parseObject(userInfo);
		LOG.info("将userInfo转换成JSON,userInfo:{}", jsonUserInfo.toJSONString());
		String encryptedData = "";
		String iv = "";
		if (jsonUserInfo.get("detail") != null && StringUtils.isNotBlank(jsonUserInfo.get("detail").toString())) {
			String detail = jsonUserInfo.get("detail").toString();

			JSONObject parseObject = JSONObject.parseObject(detail);
			encryptedData = (String) parseObject.get("encryptedData");
			iv = (String) parseObject.get("iv");
		} else {
			encryptedData = (String) jsonUserInfo.get("encryptedData");
			iv = (String) jsonUserInfo.get("iv");
		}
		LOG.info("获取解密数据解密start------------>>>解密需要的数据为:encryptedData:{},iv:{},session_key:{}", encryptedData, iv, session_key);
		// 被加密的数据
		byte[] dataByte = Base64.decodeBase64(encryptedData);
		// 加密秘钥
		byte[] aeskey = Base64.decodeBase64(session_key);
		// 偏移量
		byte[] ivByte = Base64.decodeBase64(iv);
		LOG.info("对加密数据进行Base64编码完毕------------>>>dataByte:{},aeskey:{},ivByte:{}", dataByte.toString(), aeskey.toString(),ivByte.toString());
		String newuserInfo = "";
		try {
			AES aes = new AES();
			byte[] resultByte = aes.decrypt(dataByte, aeskey, ivByte);
			if (null != resultByte && resultByte.length > 0) {
				newuserInfo = new String(resultByte, "UTF-8");
				LOG.info("解密完毕,解密结果为newuserInfo:{}", newuserInfo);
				return JSONObject.parseObject(newuserInfo);
			}
		} catch (Exception e) {
			LOG.info("解密异常!检查解密数据 {}", newuserInfo, e);
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 根据logininfo 的code尝试获取unionID
	 * 
	 * @param loginInfo
	 * @param request
	 * @return
	 */
	private  Map<String, String> getUnionIDByLoginInfo(String loginInfo, HttpServletRequest request) {
		JSONObject loginRes = null;
		Map<String, Object> loginReq = new HashMap<>();
		Map<String, String> loginResMap = new HashMap<>();

		LOG.info("--------------->>>>>进入 getUnionIDByLoginInfo");

		JSONObject loginMap = JSONObject.parseObject(loginInfo);
		LOG.info("转换JSON完毕");

		LOG.info("组装请求数据----start:");
		String resCode = (String) loginMap.get("code");
		loginReq.put("js_code", resCode);
		loginReq.put("appid", SysConstants.WECHAT_APPID); // 这几个变量是自己去获取的 我做成了配置文件中
		loginReq.put("secret", SysConstants.WECHAT_SECRET); // 去这个网址看参数说明:  https://developers.weixin.qq.com/miniprogram/dev/api/api-login.html#wxloginobject
		loginReq.put("grant_type", SysConstants.WECHAT_FRANT_TYPE);
		LOG.info("组装请求参数完毕loginReq={}", gson.toJson(loginReq));
		try {

			LOG.info("开始发送请求到微信...url={}", SysConstants.WEICHAT_UNIONID_URL);

			loginRes = HttpClientUtils.doGet(SysConstants.WEICHAT_UNIONID_URL, loginReq);
		} catch (Exception e) {
			LOG.error("调用httpClient异常!异常信息={}", e);
			throw new RuntimeException(e);
		}
		String openid = (String) loginRes.get("openid");
		String session_key = (String) loginRes.get("session_key");
		String unionid = (String) loginRes.get("unionid");
		loginResMap.put("openid", openid);
		loginResMap.put("unionid", unionid);
		loginResMap.put("session_key", session_key);
		LOG.info("https返回数据loginRes={}", loginRes.toJSONString());
		// 微信建议不要在传输中有session_key,我们在这里做一个缓存数据key=session_key_safe,value="真实的session_key+真实的openid"
		// TODO
		// 时间问题,这里暂不处理
		// loginResMap.put("session_key", "session_key_safe");

		return loginResMap;
	}

    然后是 

AES.java  其实我也是从网上找的 不过亲测可用.

package com.jd.ecc.tianfu.utils;

import java.security.AlgorithmParameters;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.Security;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.bouncycastle.jce.provider.BouncyCastleProvider;

public class AES {
	public static boolean initialized = false;

	/**
	 * AES对称解密工具类
	 * 
	 * @param content
	 *            密文
	 * @return
	 * @throws InvalidAlgorithmParameterException
	 * @throws NoSuchProviderException
	 */
	public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
		initialize();
		try {
			// java是没有
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
			Key sKeySpec = new SecretKeySpec(keyByte, "AES");

			cipher.init(Cipher.DECRYPT_MODE, sKeySpec, generateIV(ivByte));// 初始化
			byte[] result = cipher.doFinal(content);
			return result;
		} catch (NoSuchAlgorithmException e) {
			e.printStackTrace();
		} catch (NoSuchPaddingException e) {
			e.printStackTrace();
		} catch (InvalidKeyException e) {
			e.printStackTrace();
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		} catch (NoSuchProviderException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return null;
	}

	public static void initialize() {
		if (initialized)
			return;
		Security.addProvider(new BouncyCastleProvider());
		initialized = true;
	}

	// 生成iv
	public static AlgorithmParameters generateIV(byte[] iv) throws Exception {
		AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
		params.init(new IvParameterSpec(iv));
		return params;
	}
}
以上.


还是贴一下我返回的数据吧:


展开阅读全文

没有更多推荐了,返回首页