参考链接:https://blog.csdn.net/chmod_r_755/article/details/75554735
最近项目有微信登录的功能,在token验证的位置出了一些问题,我这里使用的是微信测试号。
首先是微信测试号的配置:
这里的token随便写什么都可以,唯一需要注意的就是要和后台使用的一致。
url必须是公网的地址,微信会发一些数据来测试这个接口,这个时候你需要在后台接受这个数据并且把数据返回,才能通过测试。
下面是controller的代码:
@GetMapping("/wx/wxLogin")
public void doWxLogin (HttpServletRequest request, HttpServletResponse response) {
try {
userService.doWxLogin(request, response);
} catch (Exception e) {
e.printStackTrace();
//相应的处理
}
}
下面是service的代码:
@Override
public void doWxLogin(HttpServletRequest request, HttpServletResponse response) throws IOException{
String authUrl = WxConstants.AUTH_BASE_URL + "appid=" + WxConstants.APPID
+ "&redirect_uri=" + URLEncoder.encode(WxConstants.REDIRECT_URL)
+ "&response_type=code"
+ "&scope=" + WxConstants.SCOPE
+ "&state=STATE#wechat_redirect";
String signature = request.getParameter("signature");/// 微信加密签名
String timestamp = request.getParameter("timestamp");/// 时间戳
String nonce = request.getParameter("nonce"); /// 随机数
String echostr = request.getParameter("echostr"); // 随机字符串
PrintWriter out = response.getWriter();
if (signature != null && timestamp != null && nonce != null && echostr != null) {
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
} else {
response.sendRedirect(authUrl);
}
}
service中获取到微信发来的四个参数,并且调用SignUtil的方法进行token的验证,如果已经通过验证就直接使用拼装好的url发送授权请求(这里需要使用到自己的APPID)。
SignUtil方法如下:
public class SignUtil {
private static final String token = WxConstants.TOKEN;
/**
* 校验签名
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
System.out.println("signature:" + signature + "timestamp:" + timestamp + "nonc:" + nonce);
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
Arrays.sort(arr);
StringBuilder content = new StringBuilder();
for (int i = 0; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null;
try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
System.out.println(tmpStr.equals(signature.toUpperCase()));
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}
/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}
/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
}
通过token验证后,就可以执行微信授权操作,下面是授权回调的代码:
public RecycleResult doWxCallBack(String code, HttpServletRequest request, HttpServletResponse response) throws Exception{
//通过code换取网页授权access_token
String access_token_url = WxConstants.ACCESS_TOKEN_BASE_URL
+ "appid=" + WxConstants.APPID
+ "&secret=" + WxConstants.APPSECRET
+ "&code=" + code
+ "&grant_type=authorization_code";
//发送请求
String jsonResult = HttpClientUtil.doGet(access_token_url);
if (jsonResult == null || "".equals(jsonResult)) {
logger.debug("认证失败");
return RecycleResult.build(500, "认证失败");
}
JsonObject accessJsonObject = new JsonParser().parse(jsonResult).getAsJsonObject();
//与开放平台关联要使用unionid
String openid = accessJsonObject.get("openid").getAsString();
String access_token = accessJsonObject.get("access_token").getAsString();
/**
* 微信与用户信息的绑定
*/
User user = userMapper.getUserInfoByOpenid(openid);
if (user == null) {
logger.debug("请先登录进行授权");
//先登录,然后执行绑定授权操作
return RecycleResult.build(401, "请先登录进行授权", openid);
}
/**
* 获取用户的微信信息(需scope为 snsapi_userinfo)
*/
String infoUrl = WxConstants.INFO_BASE_URL
+ "access_token=" + access_token
+ "&openid=" + openid
+ "&lang=zh_CN";
String userInfoJson = HttpClientUtil.doGet(infoUrl);
if (userInfoJson == null || "".equals(userInfoJson)) {
logger.debug("获取用户微信信息失败");
return RecycleResult.build(500, "获取用户微信信息失败");
}
//使用微信的信息
JsonObject userInfoJsonObject = new JsonParser().parse(userInfoJson).getAsJsonObject();
//返回用户信息
List infoList = new ArrayList<>();
infoList.add(userInfoJsonObject);
infoList.add(user);
//保存用户信息
userInfoMap.put(user.getUsername(), infoList);
return RecycleResult.ok(user.getUsername());
}
在访问授权的URL时,微信会返回一个code,使用code、APPID、APPSECRET换取access_token,授权通过。
上面代码中使用到的Wxconstants是笔者自己封装的一个常量类,
总结:
其实微信授权登录,就是把微信提供的一个openid(如果需要和微信开放平台整合则需要使用unionid)保存下来,这个openid来获取微信的或者是数据库中的信息,从而可以获取信息。