公众号接收事件推送

这篇具有很好参考价值的文章主要介绍了公众号接收事件推送。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

洛塔服务号回复005获取代码。

功能说明

用户订阅公众号、取消订阅、扫码公众号、开启地理位置、自定义菜单事件等,都会有收到微信发送的事件推送。本篇测试的事件推送

  • 订阅公众号:含扫码关注和其他关注方式
  • 取消订阅
  • 扫描带参数二维码
  • 上报地理位置:公众号后台需开启
  • 自定义菜单事件
  • 自定义菜单点击链接

方式选择

公众号后台的消息加解密方式选择安全模式。如果用的明文模式,需要对应调整。

  • URL:和上一篇的一模一样,本篇使用http://test.lootaa.com/lootaa-wechat/wx3
  • Token:任意填写,和代码中的一致
  • EncodingAESKey:随机生成即可,代码中要用
  • 消息加解密方式:安全模式(推荐)

其中,URL对应代码部署方式为,后台使用springboot来开发,nginx做端口转发。
nginx配置:

        location /lootaa-wechat/ {
                proxy_pass http://127.0.0.1:2022/lootaa-wechat/;
                proxy_set_header  X-Real-IP  $remote_addr;
                proxy_set_header Host $host;
        }

application.properties配置

server.port=2022
server.servlet.context-path=/lootaa-wechat

get方法访问配置(路径使用的http://test.lootaa.com/lootaa-wechat/wx3)

@RestController
public class Test005 {
	@GetMapping("wx3")
	public void wxGet(HttpServletRequest request, PrintWriter pw) {
		// 微信加密签名,需要使用本地计算出来的和这个对比,确认是微信发送的消息
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp"); // 时间戳
		String nonce = request.getParameter("nonce"); // 随机数
		String echostr = request.getParameter("echostr"); // 随机字符串
		// 将token、timestamp、nonce三个参数进行字典序排序
		List<String> list = new ArrayList<String>();
		list.add("lootaa"); // 公众号后台设置的token
		list.add(timestamp);
		list.add(nonce);
		Collections.sort(list);
		// 将三个参数字符串拼接成一个字符串进行sha1加密
		String tokenStr = "";
		for (int i = 0; i < list.size(); i++) {
			tokenStr += list.get(i);
		}
		String signatureStr = DigestUtils.sha1Hex(tokenStr);
		// 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
		if (signature.equals(signatureStr)) {
			pw.write(echostr); // 原样返回echostr参数内容
		} else {
			pw.write("");
		}
	}

打包部署到服务器后,将此路径和token填写到后台中,点击保存即可。

辅助类

为了方便解析,需要两个辅助类,分别是将接收到的request转化为Document(dom4j下的)、将Document转化为map。

  • 将request转化为Document
	public static Document getDocument(HttpServletRequest request) {
		SAXReader reader = new SAXReader();
		try {
			InputStream ins = request.getInputStream();
			Document doc = reader.read(ins);
			return doc;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return null;
	}
  • 将Document转化为map
	public static Map<String, String> docToMap(Document doc) {
		Map<String, String> map = new HashMap<String, String>();
		Element root = doc.getRootElement();
		@SuppressWarnings("unchecked")
		List<Element> list = root.elements();
		for (Element element : list) {
			map.put(element.getName(), element.getText());
		}
		return map;
	}

微信提供的辅助类

微信提供了多种语言版本的辅助类,可以点击这里直接下载。使用过程中,需要maven引入包。其中codec是必须引入的,dom4j是我测试代码解析使用的。

		<dependency>
			<groupId>commons-codec</groupId>
			<artifactId>commons-codec</artifactId>
		</dependency>
		
		<dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>

微信提供的辅助类包括

  • AesException.java
  • ByteGroup.java
  • PKCS7Encoder.java
  • SHA1.java
  • WXBizMsgCrypt.java
  • XMLParse.java

提供测试的二维码

为了方便测试带参数的二维码关注和扫描事件,需要先生成对应的二维码。本篇使用了微信提供的工具网站:https://mp.weixin.qq.com/debug/cgi-bin/apiinfo

  • 先获取token
    公众号接收事件推送

  • 用token获取到ticket
    公众号接收事件推送

  • 拼接得到二维码
    最终二维码为https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=【上一步得到的ticket】

接收普通消息解密

用到了上面提供的辅助类。得到map后,处理方式就和明文的基本一致了(除了被动回复需要加密)

		String token = "lootaa";
		String encodingAesKey = "FpKEYJDuwK92k2juU2z0sUvTmc3hB4W5wGLJEKay8oK";
		String appid = "wx276049d6a7551dca";
    	WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appid);
        String timestamp = request.getParameter("timestamp");    
        String nonce = request.getParameter("nonce");  
        String msgSignature = request.getParameter("msg_signature");  
		Document doc = getDocument(request);
		String result2 = pc.decryptMsg(msgSignature, timestamp, nonce, doc.asXML());
		System.out.println("解密后明文: " + result2);
		Map<String, String> map = docToMap(DocumentHelper.parseText(result2));

被动回复加密

result就是明文情况下要返回的消息,直接调用下encryptMsg即可(pc是加密时候的WXBizMsgCrypt、timestamp和nonce是从request中接收到的原样信息)。

result = pc.encryptMsg(result, timestamp, nonce);

公众号订阅事件

如果是普通关注公众号,并不会有参数。如果是通过扫描带参数二维码关注,则会出现字段EventKey,去掉开头的qrscene_就是二维码中的参数。

			if(Objects.equals(event, "subscribe")) { //公众号订阅
				String eventKey = map.get("EventKey");  //如果是扫码关注,同时二维码有参数,会有这个值,qrscene_开头
				if(eventKey != null) {
					System.out.println("二维码参数" + eventKey.replaceFirst("qrscene_", ""));
				}
				String ticket = map.get("Ticket"); //用来生成二维码
				if(ticket != null) {
					String code = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
					System.out.println("二维码:" + code);
				}
				String result = "<xml>" + "<ToUserName><![CDATA[" + map.get("FromUserName") + "]]></ToUserName>"
						+ "<FromUserName><![CDATA[" + map.get("ToUserName") + "]]></FromUserName>" + "<CreateTime>"
						+ System.currentTimeMillis() + "</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>"
						+ "<Content><![CDATA[欢迎关注洛塔!]]></Content></xml>";
				result = pc.encryptMsg(result, timestamp, nonce);
				pw.write(result);
			}

公众号取消订阅

			if(Objects.equals(event, "unsubscribe")) { //公众号订取消阅
				System.out.println("取消订阅");
				pw.write(nonce);
			}

已关注用户扫码事件

这里有个坑,收到推送消息后,返回给微信的如果是nonce,公众号则会提示异常。必须返回空字符串,或者success。

			if(Objects.equals(event, "SCAN")) { //已关注的用户扫码
				String eventKey = map.get("EventKey");  //qrscene_开头
				if(eventKey != null && eventKey.length() > 0) {
					System.out.println("二维码参数" + eventKey.replaceFirst("qrscene_", ""));
				}
				String ticket = map.get("Ticket"); //用来生成二维码
				if(ticket != null) {
					String code = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
					System.out.println("二维码:" + code);
				}
				pw.write("");
			}

上报地理位置事件

必须在公众号后台开启地理位置获取,开启的时候会让选择模式。
公众号接收事件推送

开启后,打开公众号的时候就有位置信息了

			if(Objects.equals(event, "LOCATION")) { //上报地理位置事件
				String latitude = map.get("Latitude");  //纬度
				String longitude = map.get("Longitude");  //经度
				String precision = map.get("Precision");  //精度,感觉没啥用
				System.out.println(latitude + "," + longitude + "," + precision);
				pw.write(nonce);
			}

点击菜单事件

实现方式可以参照之前写的自定义菜单部分。文章来源地址https://www.toymoban.com/news/detail-483860.html

			if(Objects.equals(event, "CLICK")) { //点击菜单事件
				String eventKey = map.get("EventKey");  //事件 KEY 值
				String result = "<xml>" + "<ToUserName><![CDATA[" + map.get("FromUserName") + "]]></ToUserName>"
						+ "<FromUserName><![CDATA[" + map.get("ToUserName") + "]]></FromUserName>" + "<CreateTime>"
						+ System.currentTimeMillis() + "</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>"
						+ "<Content><![CDATA[" + eventKey + "]]></Content></xml>";
				result = pc.encryptMsg(result, timestamp, nonce);
				pw.write(result);
			}

点击菜单跳转链接

			if(Objects.equals(event, "VIEW")) { //点击菜单跳转链接
				String eventKey = map.get("EventKey");  //链接地址
				System.out.println(eventKey);
				pw.write(nonce);
			}

完整代码

package com.lootaa.wechat;

import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.codec.digest.DigestUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import com.lootaa.wechat.util.WXBizMsgCrypt;

/**
 * 前置条件:基本配置中开启了服务器配置 
 * 完整项目源码可关注公众号"lootaayun"(洛塔),回复005获取
 */
@RestController
public class Test005 {

	@GetMapping("wx3")
	public void wxGet(HttpServletRequest request, PrintWriter pw) {
		// 微信加密签名,需要使用本地计算出来的和这个对比,确认是微信发送的消息
		String signature = request.getParameter("signature");
		String timestamp = request.getParameter("timestamp"); // 时间戳
		String nonce = request.getParameter("nonce"); // 随机数
		String echostr = request.getParameter("echostr"); // 随机字符串
		// 将token、timestamp、nonce三个参数进行字典序排序
		List<String> list = new ArrayList<String>();
		list.add("lootaa"); // 公众号后台设置的token
		list.add(timestamp);
		list.add(nonce);
		Collections.sort(list);
		// 将三个参数字符串拼接成一个字符串进行sha1加密
		String tokenStr = "";
		for (int i = 0; i < list.size(); i++) {
			tokenStr += list.get(i);
		}
		String signatureStr = DigestUtils.sha1Hex(tokenStr);
		// 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
		if (signature.equals(signatureStr)) {
			pw.write(echostr); // 原样返回echostr参数内容
		} else {
			pw.write("");
		}
	}

	public static Map<String, String> docToMap(Document doc) {
		Map<String, String> map = new HashMap<String, String>();
		Element root = doc.getRootElement();
		@SuppressWarnings("unchecked")
		List<Element> list = root.elements();
		for (Element element : list) {
			map.put(element.getName(), element.getText());
		}
		return map;
	}

	public static Document getDocument(HttpServletRequest request) {
		SAXReader reader = new SAXReader();
		try {
			InputStream ins = request.getInputStream();
			Document doc = reader.read(ins);
			return doc;
		} catch (IOException e) {
			e.printStackTrace();
		} catch (DocumentException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * 如果不能确保5秒钟之内回复消息,微信会发起重试。对于此种情况,两种解决方法 1. 所有消息都有MsgId字段,用来做判断避免重复处理 2.
	 * 先返回一条固定消息,然后再启线程单独处理(比如处理完成后在用客服消息接口发送给用户)
	 */
	@PostMapping("wx3")
	public void wxPost(HttpServletRequest request, HttpServletResponse response, PrintWriter pw)
			throws Exception {
		String token = "lootaa";
		String encodingAesKey = "FpKEYJDuwK92k2juU2z0sUvTmc3hB4W5wGLJEKay8oK";
		String appid = "wx276049d6a7551dca";
    	WXBizMsgCrypt pc = new WXBizMsgCrypt(token, encodingAesKey, appid);
        String timestamp = request.getParameter("timestamp");    
        String nonce = request.getParameter("nonce");  
        String msgSignature = request.getParameter("msg_signature");  
		Document doc = getDocument(request);
		String result2 = pc.decryptMsg(msgSignature, timestamp, nonce, doc.asXML());
		System.out.println("解密后明文: " + result2);
		Map<String, String> map = docToMap(DocumentHelper.parseText(result2));
		String messageType = map.get("MsgType"); //这个值如果是event表示是事件推送;如果是其他字符,参照Test004
		if(Objects.equals("event", messageType)) {
			String event = map.get("Event"); //这个是事件的具体类型
			
			if(Objects.equals(event, "subscribe")) { //公众号订阅
				String eventKey = map.get("EventKey");  //如果是扫码关注,同时二维码有参数,会有这个值,qrscene_开头
				if(eventKey != null) {
					System.out.println("二维码参数" + eventKey.replaceFirst("qrscene_", ""));
				}
				String ticket = map.get("Ticket"); //用来生成二维码
				if(ticket != null) {
					String code = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
					System.out.println("二维码:" + code);
				}
				String result = "<xml>" + "<ToUserName><![CDATA[" + map.get("FromUserName") + "]]></ToUserName>"
						+ "<FromUserName><![CDATA[" + map.get("ToUserName") + "]]></FromUserName>" + "<CreateTime>"
						+ System.currentTimeMillis() + "</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>"
						+ "<Content><![CDATA[欢迎关注洛塔!]]></Content></xml>";
				result = pc.encryptMsg(result, timestamp, nonce);
				pw.write(result);
			}
			
			if(Objects.equals(event, "unsubscribe")) { //公众号订取消阅
				System.out.println("取消订阅");
				pw.write(nonce);
			}
			
			if(Objects.equals(event, "SCAN")) { //已关注的用户扫码
				String eventKey = map.get("EventKey");  //qrscene_开头
				if(eventKey != null && eventKey.length() > 0) {
					System.out.println("二维码参数" + eventKey.replaceFirst("qrscene_", ""));
				}
				String ticket = map.get("Ticket"); //用来生成二维码
				if(ticket != null) {
					String code = "https://mp.weixin.qq.com/cgi-bin/showqrcode?ticket=" + ticket;
					System.out.println("二维码:" + code);
				}
				pw.write("");
			}
			
			if(Objects.equals(event, "LOCATION")) { //上报地理位置事件
				String latitude = map.get("Latitude");  //纬度
				String longitude = map.get("Longitude");  //经度
				String precision = map.get("Precision");  //精度,感觉没啥用
				System.out.println(latitude + "," + longitude + "," + precision);
				pw.write(nonce);
			}
			
			if(Objects.equals(event, "CLICK")) { //点击菜单事件
				String eventKey = map.get("EventKey");  //事件 KEY 值
				String result = "<xml>" + "<ToUserName><![CDATA[" + map.get("FromUserName") + "]]></ToUserName>"
						+ "<FromUserName><![CDATA[" + map.get("ToUserName") + "]]></FromUserName>" + "<CreateTime>"
						+ System.currentTimeMillis() + "</CreateTime>" + "<MsgType><![CDATA[text]]></MsgType>"
						+ "<Content><![CDATA[" + eventKey + "]]></Content></xml>";
				result = pc.encryptMsg(result, timestamp, nonce);
				pw.write(result);
			}
			
			if(Objects.equals(event, "VIEW")) { //点击菜单跳转链接
				String eventKey = map.get("EventKey");  //链接地址
				System.out.println(eventKey);
				pw.write(nonce);
			}
			
		}
		
	}

}

到了这里,关于公众号接收事件推送的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信公众号模板消息源码实现,打破服务号群发推送次数限制

    公众号服务号每个月只能群发推送四次文章,我们可以使用模板消息为公众号粉丝推送信息 下面是使用golang实现的模板消息发送类库封装,轻松实现模板消息发送 wechat.go 我们的使用方式 推送的效果如图所示,点击模板就能跳转到我们自定义的url上 我在自己客服系统中也是

    2024年02月15日
    浏览(36)
  • 69、配置AWS服务,接收来自RTSP流的推送

    基本思想:在上一篇的基础和视频教程之后,进行简单的aws服务,进行RTSP流的接收 第一步: 第二步:配置video_stream,记得选择香港节点 同时记录这个信息,后面的策略需要填充 第三步:进行策略设置 第四步:策略设置,选中右上角的创建策略 第五步、进行json填充 第六步:

    2024年02月09日
    浏览(21)
  • 企业微信消息推送(一)接收消息服务器URL

    1.点击左上角头像,打开微信管理平台 2.创建应用 3.获取五个参数 3.1获取应用的AgentId、Secret 3.2获取企业ID 配置接收消息服务器URL 企业微信限制过多,公司域名所有权检验不通过。采用接收消息服务器URL的方式。 3.3 获取token、EncodingAESKey 4.1 内网穿透、本地开发 先将请求打到

    2024年02月04日
    浏览(34)
  • SSE(服务器推送事件)规范

    SSE 是指 \\\"Server-Sent Events\\\",即服务器推送事件。它是一种基于 HTTP 的服务器推送技术,允许服务器实时向客户端推送数据。SSE 规范定义了一种在客户端和服务器之间单向实时通信的方式,通常用于实现服务器向客户端推送更新、通知或实时数据。 使用 SSE,客户端可以通过简单

    2024年01月18日
    浏览(42)
  • SpringBoot SSE服务端主动推送事件详解

    SSE(Server Sent Event),直译为服务器发送事件,也就是服务器主动发送事件,客户端可以获取到服务器发送的事件。 我们常见的 http 交互方式是客户端发起请求,服务端响应,然后一次请求完毕。但是在SSE的使用场景下,客户端发起请求,然后建立SEE连接一直保持,服务端就可

    2024年02月07日
    浏览(28)
  • 公众号自动回复消息添加跳转小程序链接

    是要实现在小程序授权公众号收发消息功能 在用户关注后自动回复消息(因为要实现授权前提是用户必须先关注了公众),在消息中添加可跳转小程序的链接,就可以将小程序的参数传到授权页面进行授权,这个授权页面是一个h5页面用webview嵌在小程序中显示,因为使用小程

    2024年02月12日
    浏览(31)
  • 微信公众号自动回复消息如何跳转小程序?

    微信公众号自动回复如何显示小程序并且支持跳转到小程序? 比如像这样 其实上也很简单,只需要一串代码即可实现。 微信公众号自动回复消息如何跳转小程序? 这样就行了。 其实就是一个类似于超链接的东西,只不过是需要几个特性的属性 data-miniprogram-appid=“wx2b38b5a0

    2024年02月07日
    浏览(39)
  • 让微信公众号自动回复可以跳转小程序的消息

    1、在公众号中关联对应的小程序 点击公众号左侧菜单栏“小程序管理”,点击“添加”按钮。 ​ 添加小程序,点击“关联小程序” ​ 输入对应的小程序名称,点击搜索,显示出对应的小程序信息,点击“下一步”按钮,用管理员的微信扫码授权即可。 ​ 2、设置公众号的

    2024年02月12日
    浏览(37)
  • 微信公众号关注回复多条信息(文字、图片、视频、跳转小程序)

    对于没做过关注回复多条信息的开发来说,没有实现的思路,花费了一天的时间研究写代码、测试,现做个记录 公众号后台配置关注自动回复只能配置一种(文字、图片、音频、视频、视频号动态) 想要多条如何实现昵?关闭自动回复出现,点击查看详情,出现如下提示 开

    2024年02月06日
    浏览(51)
  • 微信公众号自动回复设置层级以及点击文字跳转小程序

    这个功能仅在微信中有作用,所以,一些标签是微信特有的。其实这个功能就是用到了微信的 bizmsgmenu 。基本格式是 再配合上a标签,最终的样子是这样: 5e239977cff1d3893c65e87afa2a25f5.png dd8e7896144b9375103aa8be755df7aa.png 回复内容纯文字就可以 57957552652781bc221817f92052afe9.png b3ed6d03178143

    2024年02月10日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包