java获取小程序中用户的unionId的三种方式
前提条件:
想要获取unionId,必须几个小程序或者公众号在同一个主体之下,要不然没有unionId,只会生成用户的openid,可登陆下面这个微信官方平台查看
注:用户未关注公众号前端常规调用wx.login()是获取不到unionid,
解决思路(仅供参考,经实测有效):在满足以上条件下前端先调取wx.login后得到SessionKey
前端再调用wx.getUserInfo接口(这时候带上了登录态)
返回给后端wx.getUserInfo接口中获取的 vi和encryptedData +SessionKey即可获取unionId
第一种实现方式:使用weixin-java-miniapp实现微信小程序登录接口 个人认为代码最为整洁
maven 依赖
<!--微信开源封装sdk-->
<dependency>
<groupId>com.github.binarywang</groupId>
<artifactId>weixin-java-miniapp</artifactId>
<version>3.6.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
PS:不想导入lombok请给LoginRequest的字段加上getter/setter方法 去掉@Data
@Data
public class LoginRequest {
//用户登录凭证
String code;
//原始数据字符串
String signature;
//校验用户信息字符串
String rawData;
//加密用户数据
String encryptedData;
//加密算法的初始向量
String iv;
}
主要代码
import cn.binarywang.wx.miniapp.api.WxMaService;
import cn.binarywang.wx.miniapp.api.impl.WxMaServiceImpl;
import cn.binarywang.wx.miniapp.bean.WxMaJscode2SessionResult;
import cn.binarywang.wx.miniapp.bean.WxMaUserInfo;
import cn.binarywang.wx.miniapp.config.impl.WxMaDefaultConfigImpl;
import me.chanjar.weixin.common.error.WxErrorException;
/**
* @describe: 类描述信息
* @author: zwr
* @date: 2020/3/26 /13:48 PM
*/
public class WxAppLoginService {
private WxMaService getWxMaService() {
WxMaDefaultConfigImpl config = new WxMaDefaultConfigImpl();
config.setAppid( ResourceUtil.getConfigByName("wx.appId")); //这里是自己小程序中的appid,
config.setSecret(ResourceUtil.getConfigByName("wx.secret"));
config.setMsgDataFormat("JSON");
WxMaService wxMaService = new WxMaServiceImpl();
wxMaService.setWxMaConfig(config);
return wxMaService;
}
public void login(LoginRequest request) throws WxErrorException {
final WxMaService wxService = getWxMaService();
// 获取微信用户session
WxMaJscode2SessionResult session = wxService.getUserService().getSessionInfo(request.getCode());
if (null == session) {
throw new RuntimeException("login handler error");
}
// 解密用户信息
WxMaUserInfo wxUserInfo = wxService.getUserService().getUserInfo(session.getSessionKey(),
request.getEncryptedData(), request.getIv());
if (null == wxUserInfo) {
throw new RuntimeException("获取失败");
}
String unionId = wxUserInfo.getUnionId();
}
}
第二种:使用微信解密工具
在maven中的pom引入为
<!--微信解密 -->
<dependency>
<groupId>org.bouncycastle</groupId>
<artifactId>bcprov-ext-jdk15on</artifactId>
<version>1.55</version>
</dependency>
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;
import java.security.*;
public class AES {
/**
* AES解密
* @param content 密文
* @return
* @throws InvalidAlgorithmParameterException
* @throws NoSuchProviderException
*/
public static final String ALGORITHM = "AES/CBC/PKCS7Padding";
public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) throws InvalidAlgorithmParameterException {
try {
Security.addProvider(new BouncyCastleProvider());
Cipher cipher = Cipher.getInstance(ALGORITHM,"BC");
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) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return null;
}
}
import java.nio.charset.Charset;
import java.util.Arrays;
public class WxPKCS7Encoder {
private static final Charset CHARSET = Charset.forName("utf-8");
private static final int BLOCK_SIZE = 32;
public static AlgorithmParameters generateIV(byte[] iv) throws Exception{
AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
params.init(new IvParameterSpec(iv)); return params;
}
/**
* 获得对明文进行补位填充的字节.
*
* @param count 需要进行填充补位操作的明文字节个数
* @return 补齐用的字节数组
*/
public static byte[] encode(int count) {
// 计算需要填充的位数
int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
if (amountToPad == 0) {
amountToPad = BLOCK_SIZE;
}
// 获得补位所用的字符
char padChr = chr(amountToPad);
String tmp = new String();
for (int index = 0; index < amountToPad; index++) {
tmp += padChr;
}
return tmp.getBytes(CHARSET);
}
/**
* 删除解密后明文的补位字符
*
* @param decrypted 解密后的明文
* @return 删除补位字符后的明文
*/
public static byte[] decode(byte[] decrypted) {
int pad = decrypted[decrypted.length - 1];
if (pad < 1 || pad > 32) {
pad = 0;
}
return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
}
/**
* 将数字转化成ASCII码对应的字符,用于对明文进行补码
*
* @param a 需要转化的数字
* @return 转化得到的字符
*/
public static char chr(int a) {
byte target = (byte) (a & 0xFF);
return (char) target;
}
}
处理业务逻辑
package com.qujiali.service;
import javax.annotation.Resource;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import org.apache.commons.codec.binary.Base64;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.qujiali.mapper.TMemberMapper;
import com.qujiali.model.TMember;
import top.qujiali.core.Constants;
import top.qujiali.core.base.BaseService;
import top.qujiali.core.util.AES;
import top.qujiali.core.util.SecurityUtil;
import top.qujiali.core.util.WxPKCS7Encoder;
/**
* 微信小程序授权登陆
*
* @author ydl
* @date 2018年7月3日
*/
@Service
public class WxloginService extends BaseService<TMember> {
public Map wxLogin(String Code, String encryptedData, String iv) {
try {
//用map来封装想给前端返回的数据
Map map = new HashMap();
// 微信服务器的接口,其实就是微信官方文档上面的那一串url地址
String url = Constants.WECHATURLHEAD + Code + Constants.WECHATURLEDN;
// 服务端请求URL需要的Spring对象
RestTemplate restTemplate = new RestTemplate();
// 调用请求url
ResponseEntity<String> responseEntity = restTemplate.exchange(url, HttpMethod.GET, null, String.class);
if (responseEntity != null && responseEntity.getStatusCode() == HttpStatus.OK) {
// 拿到消息体
String sessionData = responseEntity.getBody();
JSONObject json = JSON.parseObject(sessionData);
// 1.sessionkey取消空格这里就是特别坑的一个地方
String data = encryptedData.replaceAll(" ", "+");
String ivstr = iv.replaceAll(" ", "+");
String sessionkey = json.get("session_key").toString();
// 2.openId 取消空格补充"+"
String openId = json.get("openid").toString();
// 3、对encryptedData加密数据进行AES解密
AES aes = new AES();
byte[] resultByte = aes.decrypt(Base64.decodeBase64(data), Base64.decodeBase64(sessionkey),
Base64.decodeBase64(ivstr));
// 4.保存数据到服务器
if (null != resultByte && resultByte.length > 0) {
String userInfo = new String(WxPKCS7Encoder.decode(resultByte));
JSONObject wxinfo = JSON.parseObject(userInfo);
Set<String> keySet = wxinfo.keySet();
for (String key : keySet) {
//这里拿到key 和value就可以获取到解密后的所有数据了你可以做你自己的业务了,保存unioId或者是openId自己处理
}
}
}
return map;
} catch (Exception e) {
throw new RuntimeException("微信登陆发生异常");
}
}
}
第三种方式: 无需导入第三方jar
一、前端需传递参数 iv encryptedData signature rawData
package com.platform.entity;
import java.io.Serializable;
/**
* @author zwr
* @email 1064562445@qq.com
* @date 2017-08-15 08:03:41
*/
public class FullUserInfo implements Serializable {
private static final long serialVersionUID = 1L;
//errMsg
private String errMsg;
//rawData
private String rawData;
//encryptedData
private String encryptedData;
//iv
private String iv;
//signature
private String signature;
}
/**
* 登录
*/
@ApiOperation(value = "登录")
@IgnoreAuth
@RequestMapping("/app/loginWeChat")
@Transactional(rollbackFor = Exception.class)
public Object loginByWeixin() {
JSONObject jsonParam = this.getJsonRequest();
logger.info("***请求参数=" + jsonParam.toString());
FullUserInfo fullUserInfo = null;
String code = "";
if (!StringUtils.isNullOrEmpty(jsonParam.getString("code"))) {
code = jsonParam.getString("code");
}
if (null != jsonParam.get("data")) {
fullUserInfo = jsonParam.getObject("data", FullUserInfo.class);
}
if (null == fullUserInfo) {
return R.error("登录失败");
}
Map<String, Object> resultObj = new HashMap<>();
//获取openid //小程序appid修改做修改 // logger.info("》》》组合token为:" + requestUrl);
String requestUrl = ApiUserUtils.getWebAccess(code);
//通过自定义工具类组合出小程序需要的登录凭证 code //System.out.println(sessionData.getString("openid"));
JSONObject sessionData = CommonUtil.httpsRequest(requestUrl, "GET", null);
if (null == sessionData || StringUtils.isNullOrEmpty(sessionData.getString("openid"))) {
return R.error("登录失败");
}
String uuid ="";
if(StringUtils.isNullOrEmpty(sessionData.getString("unionid"))){
uuid= sessionData.get("unionid").toString();
}
}
工具类
wx.webAccessTokenhttps = https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code
package com.platform.util;
import com.platform.utils.ResourceUtil;
/**
* 作者: @author zwr <br>
* 时间: 2017-08-11 08:58<br>
* 描述: ApiUserUtils <br>
*/
public class ApiUserUtils {
//替换字符串
public static String getCode(String APPID, String REDIRECT_URI, String SCOPE) {
return String.format(ResourceUtil.getConfigByName("wx.getCode"), APPID, REDIRECT_URI, SCOPE);
}
//替换字符串 验证appid和secret的正确性
public static String getWebAccess(String CODE) {
return String.format(ResourceUtil.getConfigByName("wx.webAccessTokenhttps"),
ResourceUtil.getConfigByName("wx.appId"),
ResourceUtil.getConfigByName("wx.secret"),
CODE);
}
//替换字符串
public static String getUserMessage(String access_token, String openid) {
return String.format(ResourceUtil.getConfigByName("wx.userMessage"), access_token, openid);
}
}
package com.platform.util;
import com.alibaba.fastjson.JSONObject;
import com.platform.utils.CharUtil;
import com.platform.utils.DateUtils;
import com.platform.utils.HttpUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.security.MessageDigest;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class CommonUtil {
/**
* 发送https请求
*
* @param requestUrl 请求地址
* @param requestMethod 请求方式(GET、POST)
* @param outputStr 提交的数据
* @return JSONObject(通过JSONObject.get ( key)的方式获取json对象的属性值)
*/
public static JSONObject httpsRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = {new MyX509TrustManager()};
/*
该处不对ssl证书进行验证,以免测试不通过
*/
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
//从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory();
URL url = new URL(requestUrl);
HttpsURLConnection conn = (HttpsURLConnection) url.openConnection();
conn.setSSLSocketFactory(ssf);
conn.setDoOutput(true);
conn.setDoInput(true);
conn.setUseCaches(false);
// 设置请求方式(GET/POST)
conn.setRequestMethod(requestMethod);
// 当outputStr不为null时向输出流写数据
if (null != outputStr) {
OutputStream outputStream = conn.getOutputStream();
// 注意编码格式
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 从输入流读取返回内容
InputStream inputStream = conn.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;
conn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
} catch (ConnectException ce) {
log.error("连接超时:{}", ce);
} catch (Exception e) {
log.error("https请求异常:{}", e);
}
return jsonObject;
}
}
转载注明出处,掌声送给社会人
更多推荐
所有评论(0)