intellij下使用springboot实现微信公众号回复功能
来源:互联网 发布:d800微调优化曝光 编辑:程序博客网 时间:2024/05/16 01:10
最近公司原本的公众号要发布3.0版本,所以自己申请了一个微信公众号进行测试,第一步就是实现了通过微信接收信息转发至java后台解析并回复的消息的简单功能,经过一番研究终于实现,现在贡献出代码和大家一起分享。
首先百度微信公众平台,进入官网进行公众号的注册,企业可与注册服务号,个人订阅号即可满足大部分功能,申请成功后即可进入到公众平台的相关功能界面
自动回复有两种实现方法,一种是平台自带的回复功能,另一种就是连接后台进行功能实现,如果需要连接后台并在本地调试就需要一款端口映射工具,可以将本地localhost端口映射到外网上,方便进行在公众号服务上的部署,我这里使用的是ngrok,免费下载,使用方便,这个我们后续会讲解如何使用
在进本配置中我们可以看到公众平台给我们的开发者id,需要我们自己配置的:
1.服务器地址URL:这里就是本地服务器部署成功后,通过ngrok映射出的外网地址,当项目部署到发布机上后,就可以改为正式的地址,但必须以http://或https://开头,分别支持80端口和443端口。
2.令牌:令牌是微信端和服务器端配对的基本,令牌只要满足是英文或数字,长度为3-32字符就可以,这里的令牌稍后在服务器端的要相同才可以请求成功
3.秘钥:随机生成就可以
本人使用的是intellij下的springboot框架实现的相关功能,各部分代码如下
在controller首先进入controller中的/movie/getSpecMovie方法中,此方法即是URL的方法地址,在开头定义我们的Token,和平台上的token对应,通过判断是get方法还是post方法进入不同分支方法,get方位为提交服务器配置时的验证使用,post是接收到公众号内消息进行的逻辑处理
Controller
package com.borya.controller;private String Token = "123456";@RestController@RequestMapping("/movie")public class movieController {@RequestMapping(value = "/getSpecMovie" , method = {RequestMethod.GET,RequestMethod.POST}) public void getSpecMovie(HttpServletRequest request , HttpServletResponse response){ response.setContentType("text/html;charset=utf-8"); System.out.println("进入方法"); Boolean isGet = request.getMethod().toLowerCase().equals("get"); if (isGet){ String signature = request.getParameter("signature"); String timestamp = request.getParameter("timestamp"); String nonce = request.getParameter("nonce"); String echostr = request.getParameter("echostr"); System.out.println(signature); System.out.println(timestamp); System.out.println(nonce); System.out.println(echostr); access(request, response); }else{ // 进入POST聊天处理 System.out.println("enter post"); try { // 接收消息并返回消息 acceptMessage(request, response); } catch (IOException e) { e.printStackTrace(); } } } /** * 验证url真实性 * @param request * @param response * @return */ private String access(HttpServletRequest request , HttpServletResponse response){ // 验证URL真实性 System.out.println("进入验证access"); String signature = request.getParameter("signature");// 微信加密签名 String timestamp = request.getParameter("timestamp");// 时间戳 String nonce = request.getParameter("nonce");// 随机数 String echostr = request.getParameter("echostr");// 随机字符串 List<String> params = new ArrayList<String>(); params.add(Token); params.add(timestamp); params.add(nonce); // 1. 将token、timestamp、nonce三个参数进行字典序排序 Collections.sort(params, new Comparator<String>() { @Override public int compare(String o1, String o2) { return o1.compareTo(o2); } }); // 2. 将三个参数字符串拼接成一个字符串进行sha1加密 String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2)); if (temp.equals(signature)) { try { response.getWriter().write(echostr); System.out.println("成功返回 echostr:" + echostr); return echostr; } catch (IOException e) { e.printStackTrace(); } } System.out.println("失败 认证"); return null; } private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException { // 处理接收消息 ServletInputStream in = request.getInputStream(); // 将POST流转换为XStream对象 XStream xs = SerializeXmlUtil.createXstream(); xs.processAnnotations(wechatInputMessage.class); xs.processAnnotations(wechatOutputMessage.class); // 将指定节点下的xml节点数据映射为对象 xs.alias("xml", wechatInputMessage.class); // 将流转换为字符串 StringBuilder xmlMsg = new StringBuilder(); byte[] b = new byte[4096]; for (int n; (n = in.read(b)) != -1;) { xmlMsg.append(new String(b, 0, n, "UTF-8")); } // 将xml内容转换为InputMessage对象 wechatInputMessage inputMsg = (wechatInputMessage) xs.fromXML(xmlMsg.toString()); String servername = inputMsg.getToUserName();// 服务端 String custermname = inputMsg.getFromUserName();// 客户端 long createTime = inputMsg.getCreateTime();// 接收时间 Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回时间 // 取得消息类型 String msgType = inputMsg.getMsgType(); // 根据消息类型获取对应的消息内容 if (msgType.equals(MsgType.Text.toString())) { // 文本消息 System.out.println("开发者微信号:" + inputMsg.getToUserName()); System.out.println("发送方帐号:" + inputMsg.getFromUserName()); System.out.println("消息创建时间:" + inputMsg.getCreateTime() + new Date(createTime * 1000l)); System.out.println("消息内容:" + inputMsg.getContent()); System.out.println("消息Id:" + inputMsg.getMsgId()); StringBuffer str = new StringBuffer(); str.append("<xml>"); str.append("<ToUserName><![CDATA[" + custermname + "]]></ToUserName>"); str.append("<FromUserName><![CDATA[" + servername + "]]></FromUserName>"); str.append("<CreateTime>" + returnTime + "</CreateTime>"); str.append("<MsgType><![CDATA[" + msgType + "]]></MsgType>"); str.append("<Content><![CDATA[" + 你要回复的内容 + "]]></Content>"); str.append("</xml>"); System.out.println(str.toString()); response.getWriter().write(str.toString()); } // 获取并返回多图片消息 if (msgType.equals(MsgType.Image.toString())) { System.out.println("获取多媒体信息"); System.out.println("多媒体文件id:" + inputMsg.getMediaId()); System.out.println("图片链接:" + inputMsg.getPicUrl()); System.out.println("消息id,64位整型:" + inputMsg.getMsgId()); wechatOutputMessage outputMsg = new wechatOutputMessage(); outputMsg.setFromUserName(servername); outputMsg.setToUserName(custermname); outputMsg.setCreateTime(returnTime); outputMsg.setMsgType(msgType); ImageMessage images = new ImageMessage(); images.setMediaId(inputMsg.getMediaId()); outputMsg.setImage(images); System.out.println("xml转换:/n" + xs.toXML(outputMsg)); response.getWriter().write(xs.toXML(outputMsg)); } }}
输入信息的实体类
package com.borya.bean;import com.thoughtworks.xstream.annotations.XStreamAlias;import java.io.Serializable;@XStreamAlias("xml")public class wechatInputMessage implements Serializable{ private static final long serialVersionUID = 1L; @XStreamAlias("ToUserName") private String ToUserName; @XStreamAlias("FromUserName") private String FromUserName; @XStreamAlias("CreateTime") private Long CreateTime; @XStreamAlias("MsgType") private String MsgType = "text"; @XStreamAlias("MsgId") private Long MsgId; // 文本消息 @XStreamAlias("Content") private String Content; // 图片消息 @XStreamAlias("PicUrl") private String PicUrl; // 位置消息 @XStreamAlias("LocationX") private String LocationX; @XStreamAlias("LocationY") private String LocationY; @XStreamAlias("Scale") private Long Scale; @XStreamAlias("Label") private String Label; // 链接消息 @XStreamAlias("Title") private String Title; @XStreamAlias("Description") private String Description; @XStreamAlias("Url") private String URL; // 语音信息 @XStreamAlias("MediaId") private String MediaId; @XStreamAlias("Format") private String Format; @XStreamAlias("Recognition") private String Recognition; // 事件 @XStreamAlias("Event") private String Event; @XStreamAlias("EventKey") private String EventKey; @XStreamAlias("Ticket") private String Ticket; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public Long getCreateTime() { return CreateTime; } public void setCreateTime(Long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public Long getMsgId() { return MsgId; } public void setMsgId(Long msgId) { MsgId = msgId; } public String getContent() { return Content; } public void setContent(String content) { Content = content; } public String getPicUrl() { return PicUrl; } public void setPicUrl(String picUrl) { PicUrl = picUrl; } public String getLocationX() { return LocationX; } public void setLocationX(String locationX) { LocationX = locationX; } public String getLocationY() { return LocationY; } public void setLocationY(String locationY) { LocationY = locationY; } public Long getScale() { return Scale; } public void setScale(Long scale) { Scale = scale; } public String getLabel() { return Label; } public void setLabel(String label) { Label = label; } public String getTitle() { return Title; } public void setTitle(String title) { Title = title; } public String getDescription() { return Description; } public void setDescription(String description) { Description = description; } public String getURL() { return URL; } public void setURL(String URL) { this.URL = URL; } public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; } public String getFormat() { return Format; } public void setFormat(String format) { Format = format; } public String getRecognition() { return Recognition; } public void setRecognition(String recognition) { Recognition = recognition; } public String getEvent() { return Event; } public void setEvent(String event) { Event = event; } public String getEventKey() { return EventKey; } public void setEventKey(String eventKey) { EventKey = eventKey; } public String getTicket() { return Ticket; } public void setTicket(String ticket) { Ticket = ticket; }}
输出信息的实体类
package com.borya.bean;import com.thoughtworks.xstream.annotations.XStreamAlias;@XStreamAlias("xml")public class wechatOutputMessage { @XStreamAlias("ToUserName") @XStreamCDATA private String ToUserName; @XStreamAlias("FromUserName") @XStreamCDATA private String FromUserName; @XStreamAlias("CreateTime") private Long CreateTime; @XStreamAlias("MsgType") @XStreamCDATA private String MsgType = "text"; private ImageMessage Image; public String getToUserName() { return ToUserName; } public void setToUserName(String toUserName) { ToUserName = toUserName; } public String getFromUserName() { return FromUserName; } public void setFromUserName(String fromUserName) { FromUserName = fromUserName; } public Long getCreateTime() { return CreateTime; } public void setCreateTime(Long createTime) { CreateTime = createTime; } public String getMsgType() { return MsgType; } public void setMsgType(String msgType) { MsgType = msgType; } public ImageMessage getImage() { return Image; } public void setImage(ImageMessage image) { Image = image; }}
图片信息的实体类
package com.borya.bean;import com.thoughtworks.xstream.annotations.XStreamAlias;@XStreamAlias("Image")public class ImageMessage extends MediaIdMessage{}
多媒体id的实体类
package com.borya.bean;import com.thoughtworks.xstream.annotations.XStreamAlias;public class MediaIdMessage { @XStreamAlias("MediaId") @XStreamCDATA private String MediaId; public String getMediaId() { return MediaId; } public void setMediaId(String mediaId) { MediaId = mediaId; }}
MsgType的枚举类
package com.borya.bean;public enum MsgType { Text("text"), Image("image"), Music("music"), Video("video"), Voice("voice"), Location("location"), Link("link"); private String msgType = ""; MsgType(String msgType) { this.msgType = msgType; } /** * @return the msgType */ @Override public String toString() { return msgType; }}
XML的CDATA验证类
package com.borya.bean;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;@Retention(RetentionPolicy.RUNTIME)@Target({ElementType.FIELD})public @interface XStreamCDATA {}
SHA1加密类
package com.borya.util;import java.security.MessageDigest;public final class SHA1 { private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' }; /** * Takes the raw bytes from the digest and formats them correct. * * @param bytes * the raw bytes from the digest. * @return the formatted bytes. */ private static String getFormattedText(byte[] bytes) { int len = bytes.length; StringBuilder buf = new StringBuilder(len * 2); // 把密文转换成十六进制的字符串形式 for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(bytes[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[bytes[j] & 0x0f]); } return buf.toString(); } public static String encode(String str) { if (str == null) { return null; } try { MessageDigest messageDigest = MessageDigest.getInstance("SHA1"); messageDigest.update(str.getBytes()); return getFormattedText(messageDigest.digest()); } catch (Exception e) { throw new RuntimeException(e); } }}
xml转换工具类
package com.borya.util;import com.borya.bean.XStreamCDATA;import com.thoughtworks.xstream.XStream;import com.thoughtworks.xstream.annotations.XStreamAlias;import com.thoughtworks.xstream.core.util.QuickWriter;import com.thoughtworks.xstream.io.HierarchicalStreamWriter;import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;import com.thoughtworks.xstream.io.xml.XppDriver;import java.io.Writer;import java.lang.reflect.Field;/** * xml转换工具 * 2017年12月15日15:33:52 */public class SerializeXmlUtil { public static XStream createXstream() { return new XStream(new XppDriver() { @Override public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { boolean cdata = false; Class<?> targetClass = null; @Override public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) { super.startNode(name, clazz); // 业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签 if (!name.equals("xml")) { cdata = needCDATA(targetClass, name); } else { targetClass = clazz; } } @Override protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } }); } private static boolean needCDATA(Class<?> targetClass, String fieldAlias) { boolean cdata = false; // first, scan self cdata = existsCDATA(targetClass, fieldAlias); if (cdata) return cdata; // if cdata is false, scan supperClass until java.lang.Object Class<?> superClass = targetClass.getSuperclass(); while (!superClass.equals(Object.class)) { cdata = existsCDATA(superClass, fieldAlias); if (cdata) return cdata; superClass = superClass.getClass().getSuperclass(); } return false; } private static boolean existsCDATA(Class<?> clazz, String fieldAlias) { if ("MediaId".equals(fieldAlias)) { return true; // 特例添加 morning99 } // scan fields Field[] fields = clazz.getDeclaredFields(); for (Field field : fields) { // 1. exists XStreamCDATA if (field.getAnnotation(XStreamCDATA.class) != null) { XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class); // 2. exists XStreamAlias if (null != xStreamAlias) { if (fieldAlias.equals(xStreamAlias.value()))// matched return true; } else {// not exists XStreamAlias if (fieldAlias.equals(field.getName())) return true; } } } return false; }}
项目的pom.xml
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>1</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.7</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <!--xml格式化工具--> <dependency> <groupId>com.thoughtworks.xstream</groupId> <artifactId>xstream</artifactId> <version>1.4.10-java7</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>
以上部分为功能实现的全部类文件
发布到本地服务器上后,就要开始用到刚才提到的ngrok,这款映射工具,我在刚才的官网上下载后,是个ngrok.exe文件,打开后在dom窗口内输入
ngrok http 8080
8080可以改为你的端口,但微信公众号的URL必须为80端口
然后成功则会进入到映射到的服务器界面,图中所示即为映射出的外网地址,将地址填到公众平台的URL上,加上方法名就可以了,在使用过程中不要关闭窗口,
由于该这个是随机的,每次重启都会变,所以利于我们微信调试,但是ngrok已经给我们了相关办法,就是需要我们注册使用
在官网都注册成功后,会得到一个token
在获得token后重新打开ngrok.exe,输入:ngrok -authtoken token 80其中token换成我们注册后返回的值,回车后,成功启动
但是返回的token太长了,不方便记忆,我们换一个方便记忆的名字,输入ngrok -subdomain leopard 80
回车后,成功启动,下面我们需要验证下输入域名:http://leopard.ngrok.com是否能够通过微信的URL校验步
打开微信公众平台,选择开发者中心,在右边的表单中输入相关内容,URL中输入我们刚设置过的域名
提交成功,说明我们的设置是有效的,此时就可以继续使用啦,有什么问题大家可以在下面留言
- intellij下使用springboot实现微信公众号回复功能
- intellij下使用springboot实现微信公众号回复功能
- 微信公众号实现机器人回复
- 基于springboot+mybatis的微信公众号开发第三篇-消息的接收与回复
- 公众号从菜鸟到高手的进化:微信公众平台自动回复功能
- 微信公众号回复消息-微信公众号使用教程14
- 微信公众号回复内容
- 微信公众号自定义回复
- 微信公众号接收回复
- 开启微信公众号自动回复
- 微信公众号开发自动回复
- 微信公众号开发---实现微信自定义菜单创建以及消息回复等
- Python3: Django开发微信公众号平台功能测试之普通消息回复
- 微信关键字自动回复功能实现
- 微信自动回复功能实现
- 微信公众平台回复公众号听歌
- JAVA实现微信公众账号自定义消息回复
- 微信公众平台配置服务器之后实现自动回复
- 扫码领红包
- SSLSocket的使用
- StringUtils的一些常用方法
- 松柏先生:98.5%的电商品牌只是阿里服务器上的一串程序代码!
- lintcode--重复子串
- intellij下使用springboot实现微信公众号回复功能
- 数据结构课程设计--学生成绩管理
- WaitForSingleObject函数的使用
- wpa_supplicant上行接口
- cpu之MemtoReg_Data
- 无缝滚动原生js
- K-means算法实现
- js+ajax 实现搜索下拉列表
- 人生的第一次