视频讲解
基础注册
首先你需要先注册一个你的微信公众号
然后打开下面的自动回复功能
之后进入到你的开发者中心
然后生成你的开发者密码,开发者id,以及设置你的IP白名单。
这里的IP白名单中的IP必须是一个公网IP,因为微信官方会把他们的请求发送到公网上,然后你接受到请求之后需要给这个请求做一个响应才能实现消息互通的功能。
之后开始配置你的服务器信息
首先是URL,这里的URL需要填写的是
http://ip:80/path(这里的path满足请求路径的格式即可)
或者是
https://ip:443/path
之后你的SpringBoot项目中就需要用到这些配置了
Java部分代码
我们首先对pom文件进行配置,因为微信的数据格式为xml,
所以我们需要引入能解析xml和转换xml的依赖,如下
这里我的springboot版本为2.7.7,但是这个影响应该不大,如果出现一些问题可以尝试换一下依赖版本
<!-- XML 文件读写 --> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.0.0</version> </dependency> <!-- java对象转换为xml字符串 --> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.19</version> </dependency> <dependency> <groupId>com.github.liyiorg</groupId> <artifactId>weixin-popular</artifactId> <version>2.8.30</version> </dependency> <dependency> <groupId>com.github.binarywang</groupId> <artifactId>weixin-java-mp</artifactId> <version>3.7.0</version> </dependency>
之后在项目中配置你的这些微信官方给你的信息即可
然后我刚才说过,微信会向公网上你刚才输入的地址发送请求,是一个get请求,并且会携带上一些参数,我们要做的就是解析这些参数,代码如下
下面是Controller层的代码
import lombok.extern.java.Log; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; @Log @RestController @RequestMapping("/wx") public class TokenCheckController { @Value("${wechat.mp.token}") private String token; @GetMapping("/") public String index(HttpServletResponse response, HttpServletRequest request) throws Exception { String echostr = TokenCheckUtil.checkToken(request, token); return echostr; } } @PostMapping("/") public String chatGPTproxy( HttpServletResponse response, HttpServletRequest request, @RequestBody String requestBody, @RequestParam("signature") String signature, @RequestParam("timestamp") String timestamp, @RequestParam("nonce") String nonce, @RequestParam(name = "encrypt_type", required = false) String encType, @RequestParam(name = "msg_signature", required = false) String msgSignature) { System.out.println("requestbody:----"+requestBody); return requestBody; }
下面是TokenCheckUtil工具包的代码
import javax.servlet.http.HttpServletRequest; import java.security.NoSuchAlgorithmException; import java.util.Arrays; import static cn.hutool.crypto.SecureUtil.sha1; /** * @author: 张锦标 * @date: 2023/4/2 15:10 * TokenCheckUtil类 */ public class TokenCheckUtil { public static String checkToken(HttpServletRequest request, String token) throws NoSuchAlgorithmException { String method = request.getMethod(); //微信token验证get请求 if ("GET".equals(method)) { //微信加密签名 String echostr = request.getParameter("echostr");//时阅鲛 String signature = request.getParameter("signature");//随机宁符串 String timestamp = request.getParameter("timestamp");//随机数 String nonce = request.getParameter("nonce"); String[] str = {token, timestamp, nonce}; //字典排序 Arrays.sort(str); String bigStr = str[0] + str[1] + str[2];// SHA1加密 String digest = sha1(bigStr);//对比签名 if (digest.equals(signature)) { return echostr; } else { return ""; } } return ""; } }
上面两个完成之后,只要你点击测试,微信就会向这个接口发送一个Get请求,并且其请求参数如上代码,需要对这些参数进行校验对比之后返回一个echostr,代码只需要直接按照上面的照抄即可。
然后继续看controller层的代码,有一个post请求,微信官方会将接收到的用户消息发送到这个post请求上,你只需要把get请求改为post,路径不变即可。
其请求数据在请求体中,因此需要使用的是@RequestBody注解
测试
这里我们先简单的做一个测试,把项目使用maven进行打包,然后部署到你的云服务器上面去
这里由于只是先做了一个测试,所以使用的是测试号管理,这里就不需要端口一定设定为80了,但是等真的变成上线了,那么就需要使用80端口,不过,你可以进行一下代理,比如使用nginx
之后你就可以点击提交,然后如果说按照上面的步骤的话,是没有问题的,就会出现如下情况
上面的URL配置成功之后,就可以让你的朋友去扫你的测试号二维码然后让他们关注后给你发消息了。
当他们给你发消息之后,你会接收到如下的消息,其格式为XML
首先你会收到一个订阅公众号的消息,可以看到有一个Event标签,然后当用户给你发送消息的时候,你还会收到一个Content标签,其中的内容就是用户给你发送的内容
那么此时你需要做的就是去解析XML并且获得你需要的数据
解析XML并取得需要的数据
Java实体类,用于接受请求并且封装
import lombok.Data; /** * @author: 张锦标 * @date: 2023/4/2 15:03 * ReceiveMessage类 */ @Data public class ReceiveMessage { /** * 开发者微信号 */ private String ToUserName; /** * 发送方账号(一个openid) */ private String FromUserName; /** * 消息创建时间(整形) */ private String CreateTime; /** * 消息类型 */ private String MsgType; /** * 文本消息内容 */ private String Content; /** * 消息ID 64位 */ String MsgId; /** * 消息的数据ID 消息来自文章才有 */ private String MsgDataId; /** * 多图文时第几篇文章,从1开始 消息如果来自文章才有 */ private String Idx; /** * 订阅事件 subscribe订阅 unsbscribe取消订阅 */ private String Event; }
XML工具包
import com.towelove.file.domain.wechat.ReceiveMessage; import com.towelove.file.domain.wechat.ReplyMessage; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import javax.servlet.http.HttpServletRequest; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.io.StringWriter; import java.io.StringWriter; import org.dom4j.Document; import org.dom4j.DocumentHelper; import org.dom4j.Element; import org.dom4j.io.XMLWriter; /** * @author: 张锦标 * @date: 2023/4/2 15:13 * XMLUtil类 */ public class XMLUtil { public static void main(String[] args) { String str = "" + "<xml><ToUserName><![CDATA[gh_71a0837d69a6]]></ToUserName>\n" + "<FromUserName><![CDATA[oy__X6O4BjH9QyyOcQaj55-O5Awo]]></FromUserName>\n" + "<CreateTime>1680533388</CreateTime>\n" + "<MsgType><![CDATA[text]]></MsgType>\n" + "<Content><![CDATA[123]]></Content>\n" + "<MsgId>24059451823534879</MsgId>\n" + "</xml>"; System.out.println(XMLUtil.XMLTOModel(str)); } public static ReceiveMessage XMLTOModel(String str) { ReceiveMessage receiveMessage = new ReceiveMessage(); try { Document document = DocumentHelper.parseText(String.valueOf(str)); Element root = document.getRootElement(); receiveMessage.setToUserName(root.elementText("ToUserName")); receiveMessage.setFromUserName(root.elementText("FromUserName")); receiveMessage.setMsgType(root.elementText("MsgType")); receiveMessage.setContent(root.elementText("Content")); receiveMessage.setCreateTime(root.elementText("CreateTime")); receiveMessage.setMsgId(root.elementText("MsgId")); //receiveMessage.setMsgDataId(root.elementText("MsgDataId")); //receiveMessage.setIdx(root.elementText("Idx")); 关注 //receiveMessage.setEvent(root.elementText("Event")); } catch (Exception e) { System.out.println(e); } return receiveMessage; } public static String ObjToXml(ReplyMessage obj) throws Exception { Document document = DocumentHelper.createDocument(); Element root = document.addElement(obj.getClass().getSimpleName()); convertObjectToXml(obj, root); StringWriter stringWriter = new StringWriter(); XMLWriter writer = new XMLWriter(stringWriter); writer.write(document); writer.close(); return stringWriter.toString(); } private static void convertObjectToXml(Object obj, Element element) throws Exception { Class<?> clazz = obj.getClass(); for (java.lang.reflect.Field field : clazz.getDeclaredFields()) { field.setAccessible(true); Element child = element.addElement(field.getName()); Object value = field.get(obj); if (value != null) { if (value.getClass().isPrimitive() || value.getClass() == java.lang.String.class) { child.setText(value.toString()); } else { convertObjectToXml(value, child); } } } } }
然后我们使用测试方法来测试一下这段解析代码是否有效