小程序前端调用wx.login接口可获得code
小程序前端调用wx.getUserProFile 可获得encryptedData 和 iv
java后端在获取到对应的数据后访问:https://api.weixin.qq.com/sns/jscode2session换取openid和session_key(用于对数据解码),然后对数据进行解码。
代码如下:
@RequestMapping(value = "login.html") public ResponseVO login(HttpServletRequest request) { String encryptedData = request.getParameter("encryptedData"); String iv = request.getParameter("iv"); String code = request.getParameter("code"); if (StringTool.checkedString(encryptedData, iv, code)) { return ResponseVO.failure("请同意微信读取相关信息!"); } String appid = "小程序appId"; String appSecret = "小程序对应的appSecret"; String apiUrl = "https://api.weixin.qq.com/sns/jscode2session?appid=" + appid + "&secret=" + appSecret + "&js_code=" + code + "&grant_type=authorization_code"; String responseBody = HttpsUtil.doGet(apiUrl); Map<String, String> jsonObject = new Gson().fromJson(responseBody, Map.class); if (!StringTool.checkedString(jsonObject.get("openid")) && !StringTool.checkedString(jsonObject.get("session_key"))) { //查询用户是否存在 PlayerInfo playerInfo = playerInfoService.getMemberByUId(jsonObject.get("openid")); if (playerInfo == null) { //解密获取用户信息 JSONObject userInfoJSON = WechatGetUserInfoUtil.getUserInfo(encryptedData, jsonObject.get("session_key"), iv); if (userInfoJSON != null) { playerInfo = new PlayerInfo(); playerInfo.setUNickName(userInfoJSON.get("nickName").toString());//昵称 playerInfo.setUId(jsonObject.get("openid"));//openId playerInfo.setUCity(userInfoJSON.get("city").toString());//城市 playerInfo.setUSex(Integer.parseInt(userInfoJSON.get("gender").toString()));//性别 playerInfo.setULogo(userInfoJSON.get("avatarUrl").toString());//头像 playerInfoService.addPlayer(playerInfo); } } return ResponseVO.success(playerInfo); } return ResponseVO.failure("登录失败!"); }
HttpsUtil代码如下:
import java.io.IOException; import java.util.Map; import com.google.gson.Gson; import org.apache.http.HttpEntity; import org.apache.http.HttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClientBuilder; import org.apache.http.util.EntityUtils; public class HttpsUtil { public static String doGet(String url) { CloseableHttpClient httpclient = HttpClientBuilder.create().build(); HttpGet httpget = new HttpGet(url); String jsonObj = null; try { HttpResponse response = httpclient.execute(httpget); HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity, "UTF-8"); jsonObj = result; } } catch (IOException e) { e.printStackTrace(); } return jsonObj; } public static Map<String, Object> doPost(String url, String json) { CloseableHttpClient httpclient = HttpClientBuilder.create().build(); HttpPost httpPost = new HttpPost(url); Map<String, Object> jsonObj = null; try { StringEntity stringEntity = new StringEntity(json, "UTF-8"); httpPost.setEntity(stringEntity); httpPost.addHeader("Accept", "application/json"); httpPost.setHeader("content-type", "application/json"); HttpResponse response = httpclient.execute(httpPost); HttpEntity entity = response.getEntity(); if (entity != null) { String result = EntityUtils.toString(entity, "UTF-8"); jsonObj = new Gson().fromJson(result, Map.class); } } catch (IOException e) { e.printStackTrace(); } return jsonObj; } }
StringTool代码如下:
/** * @version v1.0 * @Description: TODO(字符串检测工具类) * @Author: jyj * @Date: 2020/3/25 0025 11:05 */ public class StringTool { /*** * TODO:检测String是否有为空的情况 * * @param strings * @return */ public static boolean checkedString(String... strings) { for (String str : strings) { if (str == null || str.trim().length() == 0 || str.equals("undefined")) { return true; } } return false; } }
WechatGetUserInfoUtil代码如下:
import com.alibaba.fastjson.JSON; import com.alibaba.fastjson.JSONObject; import org.bouncycastle.jce.provider.BouncyCastleProvider; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.Base64Utils; 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 java.io.UnsupportedEncodingException; import java.security.*; import java.security.spec.InvalidParameterSpecException; import java.util.Arrays; import java.util.Base64; /** * @author lqx * @create 2018-08-07 8:30 */ public class WechatGetUserInfoUtil { //日志记录器 private static final Logger log = LoggerFactory.getLogger(WechatGetUserInfoUtil.class); /** * 解密用户敏感数据获取用户信息 * * @param sessionKey 数据进行加密签名的密钥 * @param encryptedData 包括敏感数据在内的完整用户信息的加密数据 * @param iv 加密算法的初始向量 * @return */ public static JSONObject getUserInfo(String encryptedData, String sessionKey, String iv) { // 被加密的数据 byte[] dataByte = Base64Utils.decode(encryptedData.getBytes()); // 加密秘钥 byte[] keyByte = Base64Utils.decode(sessionKey.getBytes()); // 偏移量 byte[] ivByte = Base64Utils.decode(iv.getBytes()); 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"); return JSON.parseObject(result); } } catch (NoSuchAlgorithmException e) { log.error(e.getMessage(), e); } catch (NoSuchPaddingException e) { log.error(e.getMessage(), e); } catch (InvalidParameterSpecException e) { log.error(e.getMessage(), e); } catch (IllegalBlockSizeException e) { log.error(e.getMessage(), e); } catch (BadPaddingException e) { log.error(e.getMessage(), e); } catch (UnsupportedEncodingException e) { log.error(e.getMessage(), e); } catch (InvalidKeyException e) { log.error(e.getMessage(), e); } catch (InvalidAlgorithmParameterException e) { log.error(e.getMessage(), e); } catch (NoSuchProviderException e) { log.error(e.getMessage(), e); } return null; } }
以上代码居多,不喜原理,能看到这都是遇到问题才来的。其实原理很简单,就是通过前端传入的encryptedData已经包含了对应的用户数据,只是需要重新通过拿到session_key对数据进行解密而已。如有缺少相关类的请留言。