福兮祸所伏,祸兮福所倚
关于微信授权这块都有固定的几个步骤
- 首先调用官方API获取登录凭证code
- 通过code去换取openId(openId就是当前用户针对于微信官方(某一环境 比如小程序 网页等 要和unionId区分开)的唯一)和session_key等信息
- 通过 openId去获取用户基本信息或者私密信息
接下来我们就来完成第一步(获取code):
来看官方给的API解释和实例吧
调用接口获取登录凭证(code)。通过凭证进而换取用户登录态信息,包括用户的唯一标识(openid)及本次登录的会话密钥(session_key)等。用户数据的加解密通讯需要依赖会话密钥完成。更多使用方法详见 小程序登录。
wx.login({
success (res) {
if (res.code) {
//发起网络请求
} else {
console.log('登录失败!' + res.errMsg)
}
}
})
很简单吧 我们直接调用wx.login 微信服务器就会返回对应的code(会变化的, 有时间限制)到这里的话 咱们第一步就完成
已经可以拿到code res.code就是咱们需要的。
接下来我们就来完成第二步(通过code会获取相对应的openId):
首先来理下思路: code我们已经获取到了 那上面在获取code之后 官方有行注释:(// 发起网络请求)这个是干嘛用的呢???
网络请求肯定是往服务器发 谁的服务器呢? 我们是针对微信来开发的 也就是和微信服务器来交互 那直接wx.request像微信发
送不就行了吗 但是:官方是不建议在小程序端发送这样的请求 ,所以我们就改变思路 用我们的服务端去和微信服务器去交互
那现在我们就差个请求地址对不对 稍等 马上奉上
auth.code2Session
本接口应在服务器端调用,详细说明参见服务端API。
登录凭证校验。通过 wx.login 接口获得临时登录凭证 code 后传到开发者服务器调用此接口完成登录流程。更多使用方法详见 小程序登录。
请求地址
GET https://api.weixin.qq.com/sns/jscode2session?appid=APPID&secret=SECRET&js_code=JSCODE&grant_type=authorization_code
- 首先请求类型是GET 不要搞错了
- 其次https://api.weixin.qq.com/sns/jscode2session是请求地址
- 再次?后面是必要参数 要做相对应的替换
好了, 目前的话 咱们的思路相当清晰 来上服务端的代码
@ApiOperation("获取微信登录凭证")
@ApiImplicitParam(name = "code", value = "编号", required = true, dataType = "String", paramType = "query")
@GetMapping("/app/weChat/session")
public JsonResult<WeChatSessionVO> getWeChatSession(@RequestParam String code,@RequestParam String appId) {
log.debug("进入/app/weChat/session");
WeChatSessionVO weChatSessionVO = this.userService.getWeChatSession(code, appId);
return JsonResultTool.success(weChatSessionVO);
}
其中getWeChatSession方法
@Override
public WeChatSessionVO getWeChatSession(String code, String appId) {
logger.debug("当前你的code是:" + code);
logger.debug("当前你的请求appId是:" + appId);
WeChatSessionVO sessionVo = new WeChatSessionVO();
Map<String, Object> wxParams = new HashMap<>();
JSONObject json = new JSONObject();
// 判断是哪种类型的小程序 (头条是tt 微信vx)
if (appId.startsWith("tt")) {
// 当前appId为头条的appId
wxParams = pickTtParams(code, appId);
json = sendGetBackJson(weChatConfig.getTokenUrlTt(), wxParams, null);
} else {
wxParams = pickWxParams(code, true, appId);
json = sendGetBackJson(weChatConfig.getTokenUrl(), wxParams, null);
logger.debug("获取到的json数据信息:" + json.toJSONString());
if (json.containsKey("unionid")) {
sessionVo.setUnionId(json.getString("unionid"));
}
}
if (json.containsKey("session_key")) {
sessionVo.setSessionKey(json.getString("session_key"));
}
sessionVo = setOpendId(sessionVo, json);
return sessionVo;
}
其中pickWxParams和sendGetBackJson
/**
*
* @param code
* @param isJsCode 是否为小程序code
* @return
*/
public Map<String, Object> pickWxParams(String code, Boolean isJsCode, String appId) {
Map<String, Object> params = new HashMap<>();
// 如果是小程序的话
params = pickWxAppIdSecret(isJsCode, params, appId);
params.put(isJsCode ? WxConstanst.JS_CODE_STR : WxConstanst.CODE_STR, code);
params.put(WxConstanst.GRANT_TYPE_STR, WxConstanst.GRANT_TYPE_VALUE);
return params;
}
private Map<String, Object> pickWxAppIdSecret(Boolean isJsCode, Map<String, Object> params, String appId) {
if (isJsCode) {
logger.debug("isJsCode==true");
// 判断appId 确认是哪个小程序
if (weChatConfig.getAppId().equals(appId)) {
params.put(WxConstanst.APPID_STR, weChatConfig.getAppId());
params.put(WxConstanst.SECRET_STR, weChatConfig.getAppSecret());
} else if (weChatConfig.getAppIdSk().equals(appId)) {
params.put(WxConstanst.APPID_STR, weChatConfig.getAppIdSk());
params.put(WxConstanst.SECRET_STR, weChatConfig.getAppSecretSk());
} else {
params.put(WxConstanst.APPID_STR, weChatConfig.getAppIdAI());
params.put(WxConstanst.SECRET_STR, weChatConfig.getAppSecretAI());
}
} else {
params.put(WxConstanst.APPID_STR, WxConstanst.APPID);
params.put(WxConstanst.SECRET_STR, WxConstanst.SECRET);
}
return params;
}
/**
* 发送get请求 返回json数据
* @param url
* @param wxParams
* @param headers
* @return
*/
public JSONObject sendGetBackJson(String url, Map<String, Object> wxParams, Map<String,String> headers) {
String result = HttpUtils.sendGet(url, wxParams, headers);
JSONObject json = JSONObject.parseObject(result);
return json;
}
sendGet
/**
* 发送get请求
* @param urlParam url地址
* @param params 参数
* @return
*/
public static String sendGet(String urlParam, Map<String, Object> params,Map<String,String> headers) {
StringBuffer resultBuffer = null;
// 构建请求参数
StringBuffer sbParams = new StringBuffer();
if (params != null && params.size() > 0) {
for (Map.Entry<String, Object> entry : params.entrySet()) {
sbParams.append(entry.getKey());
sbParams.append("=");
sbParams.append(entry.getValue());
sbParams.append("&");
}
}
HttpURLConnection con = null;
BufferedReader br = null;
try {
URL url = null;
if (sbParams != null && sbParams.length() > 0) {
url = new URL(urlParam + "?" + sbParams.substring(0, sbParams.length() - 1));
} else {
url = new URL(urlParam);
}
con = (HttpURLConnection) url.openConnection();
if(headers != null){
Iterator entries = headers.entrySet().iterator();
while (entries.hasNext()){
Map.Entry entry = (Map.Entry) entries.next();
String key = (String)entry.getKey();
String value = (String)entry.getValue();
con.setRequestProperty(key,value);
}
}
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.connect();
resultBuffer = new StringBuffer();
br = new BufferedReader(new InputStreamReader(con.getInputStream(), "utf-8"));
String temp;
while ((temp = br.readLine()) != null) {
resultBuffer.append(temp);
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
if (br != null) {
try {
br.close();
} catch (IOException e) {
br = null;
throw new RuntimeException(e);
} finally {
if (con != null) {
con.disconnect();
con = null;
}
}
}
}
return resultBuffer.toString();
}
setOpenId
public WeChatSessionVO setOpendId(WeChatSessionVO sessionVo, JSONObject json) {
if (json.containsKey(WxConstanst.OPENDID_STR)) {
sessionVo.setOpenId(json.getString(WxConstanst.OPENDID_STR));
}
return sessionVo;
}
这样操作下来 我们服务端就拿到了openId和sessionkey
那接下来咱们来操作第3步
通过 openId去获取用户基本信息或者私密信息:
withCredentials如果为false的话 是获取到昵称等 为true的时候 可以获取到加密信息
在小程序端直接wx.getUserInfo 然后拿到签名和加密数据 咱们去服务端解密 获取详细信息 服务端代码来了
public void getUnionIDAndSaveUser(UserDTO userDTO) throws Exception{
// 解密
logger.debug("当前的userDTO.getEncryptedData():" + userDTO.getEncryptedData());
logger.debug("当前的userDTO.getSessionKey():" + userDTO.getSessionKey());
logger.debug("当前的userDTO.getIv():" + userDTO.getIv());
String result = AesCbcUtil.decrypt(userDTO.getEncryptedData(), userDTO.getSessionKey(), userDTO.getIv(),"UTF-8");
logger.debug("当前的result=====================================》》》》》》》:" + result);
if (null != result && result.length() > 0) {
JSONObject jsonObject = JSONObject.parseObject(result);
if (jsonObject.containsKey("unionId")) {
logger.debug("MAP当前的unionId:" + jsonObject.get("unionId").toString());
userDTO.setUnionId(jsonObject.get("unionId").toString());
} else {
logger.debug("当前的unionId:" + result);
if (userDTO.getAppId() != null && userDTO.getAppId().startsWith(weChatConfig.getAppIdTt())) {
} else {
throw new Exception("json字符串不包含unionId");
}
}
} else {
throw new Exception("获取unionId失败");
}
this.save(userDTO);
}
decrypt方法
/**
* AES解密
*
* @param data //密文,被加密的数据
* @param key //秘钥
* @param iv //偏移量
* @param encodingFormat //解密后的结果需要进行的编码
* @return
* @throws Exception
*/
public static String decrypt(String data, String key, String iv, String encodingFormat) throws Exception {
// initialize();
//被加密的数据
byte[] dataByte = Base64.decodeBase64(data);
//加密秘钥
byte[] keyByte = Base64.decodeBase64(key);
//偏移量
byte[] ivByte = Base64.decodeBase64(iv);
try {
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
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, encodingFormat);
return result;
}
return null;
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (NoSuchPaddingException e) {
e.printStackTrace();
} catch (InvalidParameterSpecException e) {
e.printStackTrace();
} catch (InvalidKeyException e) {
e.printStackTrace();
} catch (InvalidAlgorithmParameterException e) {
e.printStackTrace();
} catch (IllegalBlockSizeException e) {
e.printStackTrace();
} catch (BadPaddingException e) {
e.printStackTrace();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
return null;
}
最后直接调用自己的save方法 就可以保存数据了 对了 如果你昵称中出现了特殊的表情 我这里也处理了 直接转为base64存库 使用的时候 再转过来发给小程序端 微信授权的大体就是这样 思路可用于H5授权等 希望可以帮到你们 点波关注或者打赏下 老铁们