文章目录
(一)外网映射工具
natapp 基于ngrok的反向代理软件,通过在公网和本地运行的 Web 服务器之间建立一个安全的通道。该工具提供了免费的网络通道进行外网映射。不过免费的域名是随机生成的,每次启动都会变动,如果需要固定域名需要付费,这个就看个人选择了。
注册网站:https://natapp.cn/
注册成功后选择隧道并配置基础的信息
下载本地的客户端工具
之后natapp.exe同级目录下新建config.ini文件
#将本文件放置于natapp同级目录 程序将读取 [default] 段
#在命令行参数模式如 natapp -authtoken=xxx 等相同参数将会覆盖掉此配置
#命令行参数 -config= 可以指定任意config.ini文件
[default]
authtoken=46ea912760053247 #对应一条隧道的authtoken
clienttoken= #对应客户端的clienttoken,将会忽略authtoken,若无请留空,
log=stdout #log 日志文件,可指定本地文件, none=不做记录,stdout=直接屏幕输出 ,默认为none
loglevel=DEBUG #日志等级 DEBUG, INFO, WARNING, ERROR 默认为 DEBUG
http_proxy= #代理设置 如 http://10.123.10.10:3128 非代理上网用户请务必留空
只填authtoken即可
这个本地端口,需要根据你具体应用开启的端口决定
构建配置文件后保存,并在natapp后台配置映射端口,配置完成后启动natapp.exe即可,记住调整info即可,打印出来的映射域名在DEBUG级别
上图红色边框显示就是生成的外网地址了,根据这个地址就可以在互联网访问你的应用了
(二)微信数据交互原理
微信公众号有俩种模式,一种是编辑模式即上面提到的通过公众号后台面板直接操作的菜单方式,第二个开发模式就是通过集成自己的后台服务器程序进行消息处理和响应。俩种模式是互斥的,当开启了开发模式编辑模式就会关闭。
其中微信后台是微信的服务器提供的服务,微信公众号服务器就是指的是我们自己开发的程序服务,上图表示在用户通过微信公众号与我们开发的服务交互的时候,都是需要通过微信后台进行中间转发的,其中中间转发就是通过XML的数据格式。
(三)开发模式接入
1. 填写服务器配置
登录微信公众平台官网后,在公众平台官网的开发-基本设置页面,勾选协议成为开发者,点击“修改配置”按钮,填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
【1】需要填入就是你提供给微信后台的认证接口,是接口的完整路径,请求方式为GET
【2】Token是自己自定义的字符串,这个本地开发的时候也需要和这个保持一致
【3】随机生成的秘钥
2. 验证服务器地址的有效性
开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:
开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:
1)将token、timestamp、nonce三个参数进行字典序排序
2)将三个参数字符串拼接成一个字符串进行sha1加密
3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
@RestController
@Slf4j
public class WxController {
private String token = "demo";
/**
* 微信认证
* 将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密
* 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
*/
@GetMapping(value = "/wx/auth")
@ResponseBody
public String auth(HttpServletRequest request) {
String signature = request.getParameter("signature");
String timestamp = request.getParameter("timestamp");
String nonce = request.getParameter("nonce");
String echostr = request.getParameter("echostr");
log.info("微信认证");
if (CheckUtil.checkSignature(signature, timestamp, nonce, token)) {
//如果校验成功,将得到的随机字符串原路返回
return echostr;
}
return null;
}
}
public class CheckUtil {
public static boolean checkSignature(String signature, String timestamp, String nonce, String tooken) {
//1.定义数组存放tooken,timestamp,nonce
String[] arr = {tooken, timestamp, nonce};
//2.对数组进行排序
Arrays.sort(arr);
//3.生成字符串
StringBuilder sb = new StringBuilder();
for (String s : arr) {
sb.append(s);
}
//4.sha1加密
String temp = getSha1(sb.toString());
if (temp != null) {
//5.将加密后的字符串,与微信传来的加密签名比较,返回结果
return temp.equals(signature);
}
return false;
}
public static String getSha1(String str) {
if (str == null || str.length() == 0) {
return null;
}
char[] hexDigits = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
'a', 'b', 'c', 'd', 'e', 'f'};
try {
MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
mdTemp.update(str.getBytes("UTF-8"));
byte[] md = mdTemp.digest();
int j = md.length;
char[] buf = new char[j * 2];
int k = 0;
for (int i = 0; i < j; i++) {
byte byte0 = md[i];
buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
buf[k++] = hexDigits[byte0 & 0xf];
}
return new String(buf);
} catch (Exception e) {
return null;
}
}
}
注意:服务地址填写的是完整的URL地址,不是仅填写域名,这意味着你请求断点都是可以自定义的,并且可以有自己的其他的业务处理逻辑
3. 开发模式下消息的接收和响应
(1)说明
官方参考文档:
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_standard_messages.html
微信的消息都是通过XML的协议进行消息的传递,所以我们在接受和发送消息的时候需要通过一个XML的工具处理消息,网上有很多这样的工具类不再详述
(2)接收和响应消息
当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。
/**
* 接收公众号消息接口
*/
@PostMapping(value = "/wx/auth")
@ResponseBody
public String getMessage(HttpServletRequest request) throws Exception {
String resultXml = null;
Map<String, String> map = MessageFormat.xmlToMap(request);
String toUserName = map.get("ToUserName");
String fromUserName = map.get("FromUserName");
String createTime = map.get("CreateTime");
String msgType = map.get("MsgType");
String content = map.get("Content");
if ("text".equals(msgType)) {
//如果接收的是文本消息,传输时所有字段都需要传递
Map<String, String> sendMap = new HashMap<>();
sendMap.put("ToUserName", fromUserName);
sendMap.put("FromUserName", toUserName);
sendMap.put("MsgType", "text");
sendMap.put("Content", "Hello World");
sendMap.put("CreateTime", String.valueOf(new Date().getTime()));
sendMap.put("MsgId", "12345678940123456");
resultXml = MessageFormat.mapToXml(sendMap);
}
log.info(resultXml);
return resultXml;
}
【注意】
当我们在构建响应的消息时,如果没有使用实体对象的而只是使用Map转XML,注意必须传递微信XML的所有字段,否则将无法正常传输,显示服务器错误,建议还是构建一个对象存储这样就不会有问题了。
(3)事件推送
【消息类型】
上面的示例中仅使用了text类型,除了text之外,还可以使用图片消息image,语音消息voice,视频消息video,链接消息link,地理位置消息location
【事件推送】event
关注:subscribe
取消关注:unsubscribe
菜单点击:CLICK,VIEW
参考文档:
https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Receiving_event_pushes.html