微信测试号接入微信,并验证token
基于java语言的微信测试号接入。
1) 申请微信测试号
【链接】微信公众平台接口测试帐号申请
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login
直接扫码登录
2)本地代码编写
当前使用的代码示例,使用servlet来编写的,用于接收微信服务器发送的请求,验证相关参数,确定发送者为微信端。
package web;
import service.WeChatCheck;
import javax.servlet.ServletException;
import javax.servlet.ServletInputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.BufferedInputStream;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @Description
* @Author zc
* @Date 2020/7/28 下午4:23
*/
@WebServlet("/wx")
public class WeChat extends HttpServlet {
/**
* 校验微信请求
* */
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String signature = req.getParameter("signature");
String timestamp = req.getParameter("timestamp");
String nonce = req.getParameter("nonce");
String echostr = req.getParameter("echostr");
//校验请求是否来自微信
boolean check = WeChatCheck.check(signature, timestamp, nonce);
//如果来自微信,将echostr原样返回,表示接入微信成功
if (check){
PrintWriter writer = resp.getWriter();
writer.write(echostr);
writer.flush();
if (writer!=null){
writer.close();
}
}
}
/**
* 校验成功后,公众号接收到的消息会以POST请求发送到当前路由
* */
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletInputStream inputStream = req.getInputStream();
//使用字节流读取公众号接收到的消息
byte[] bytes = new byte[1024];
int len = 0;
StringBuffer sb = new StringBuffer();
while ((len = inputStream.read(bytes)) != -1){
sb.append(new String(bytes,0,len));
}
System.out.println(sb.toString());
}
}
微信请求数据验证
package service;
import jdk.nashorn.internal.runtime.FindProperty;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
/**
* @Description
* @Author zc
* @Date 2020/7/28 下午5:09
*/
public class WeChatCheck {
//token字段与微信开发页面填写的token保持一致
private static final String TOKEN = "abac";
/**
* 校验微信微信接入是否成功
* signature:微信服务器{token,timestamp,nonce}经过sha1加密后的字段(微信签名)
*
* 本地使用token加微信服务器发送过来的timestamp,nonce进行字段排序后,经过sha1加密(本地签名)
*
* 如果 微信签名 == 本地签名
* 表示微信服务器中的token,与本地的token是同一个token
* 微信接入成功
* */
public static boolean check(String signature,String timestamp,String nonce){
String[] strings = new String[]{TOKEN,timestamp,nonce};
System.out.println("排序前");
for (String str : strings){
System.out.println(str);
}
//字典排序
Arrays.sort(strings);
System.out.println("排序后");
for (String str : strings){
System.out.println(str);
}
//将strings进行sha1加密后与signature比较
String encrypt = encrypt(strings);
System.out.println("微信签名"+signature);
System.out.println("加密签名"+encrypt);
return encrypt.equals(signature);
}
/***
* 将{TOKEN,timestamp,nonce}字符数组按字典排序,合并成一个字符串
* 获取合并后字符串,获取其字节流(byte)
* 获取sha1加密对象,将字节流数组加密
*
* 加密后的字节流进行简单处理,将其转换为16进制的字符串
* 每一个byte按照4bit拆分,然后转换为16进制字符串
*
*/
private static String encrypt(String[] strings){
StringBuffer str = new StringBuffer();
for (String s : strings){
str.append(s);
}
try {
//获取sha1加密对象
MessageDigest md = MessageDigest.getInstance("sha1");
//获取字符串的byte数组,并加密
byte[] digest = md.digest(str.toString().getBytes());
//声明0~f的字符数组
char[] chars = {'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'};
//返回结果字符串拼接
StringBuffer sb = new StringBuffer();
//循环加密后的byte数组,转换为16进制的字符数组
for (byte b : digest){
sb.append(chars[(b>>4)&15]);
sb.append(chars[(b&15)]);
}
return sb.toString();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
return null;
}
}
}
在编写好代码,运行起来,在确保能够通过域名访问后,填写URL和token。点击提交后,微信会发送请求到填写的URL,携带四个参数。
参数 | 描述 | 作用 |
---|---|---|
signature | signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 | token+timestamp+nonce进行字典排序后,再使用sha1加密后生成的字符串 |
timestamp | 时间戳 | 当前demo不关心 |
nonce | 随机数 | 当前demo不关心 |
echostr | 随机字符串 | 进行验证后,返回给微信端的数据 |
如果能够成功接收到微信发送的GET请求,并完成Token验证后,返回相应的结果,页面会提示配置成功。
URL:在填写URL前,需要保证URL能够访问到,也就是说,这里的URL是一个能够在网络上访问到的地址,可以使用域名+云服务器,也可以使用内网穿透。
Token:token值的填写没有规则限制,但是在本地代码中需要使用到,用于验证微信连接。