前提条件:

想要获取unionId,必须几个小程序或者公众号在同一个主体之下,要不然没有unionId,只会生成用户的openid,可登陆下面这个微信官方平台查看

https://open.weixin.qq.com

注:用户未关注公众号前端常规调用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;
    }

}

转载注明出处,掌声送给社会人

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐