效果图

1.准备工作

申请微信订阅号(个人只能申请订阅号,而且没什么功能,也无法认证),申请完毕,点击 开发=>基本配置,如下图:

服务器配置需要有 域名 80端口,我猜你没有,这里推荐个实用工具,pagekite,下载链接,

这个工具需要 python2.7以上环境,还有邮箱一个,一个邮箱一个月,邮箱这东西大家懂得,

用pagekite申请完域名,就可以用自己的电脑做订阅号服务器了.

2.服务器代码

创建个springboot工程

pom.xml

<properties>
<project.build.sourceEncoding>UTF-</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5..RELEASE</version>
</parent> <dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!-- 使thymeleaf支持h5标签 -->
<dependency>
<groupId>net.sourceforge.nekohtml</groupId>
<artifactId>nekohtml</artifactId>
<version>1.9.</version>
</dependency>

    <dependency>
      <groupId>dom4j</groupId>
      <artifactId>dom4j</artifactId>
      <version>1.6.1</version>
    </dependency>

    </dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>

入口类

@SpringBootApplication
public class WeChatApplication { public static void main(String[] args) {
SpringApplication.run(WeChatApplication.class, args);
System.out.println("====启动成功====");
} }

application.properties,配置server.port=80

Controller,这个是处理微信请求,get方法就是微信公众平台 服务器配置url请求的路径,post是处理用户事件

@RestController
public class WeChatController {
@Autowired
private WeChatService weChatService;
/**
* 处理微信服务器发来的get请求,进行签名的验证
*
* signature 微信端发来的签名
* timestamp 微信端发来的时间戳
* nonce 微信端发来的随机字符串
* echostr 微信端发来的验证字符串
*/
@GetMapping(value = "wechat")
public String validate(@RequestParam(value = "signature") String signature,
@RequestParam(value = "timestamp") String timestamp,
@RequestParam(value = "nonce") String nonce,
@RequestParam(value = "echostr") String echostr) {
return WeChatUtil.checkSignature(signature, timestamp, nonce) ? echostr : null; }
/**
* 此处是处理微信服务器的消息转发的
*/
@PostMapping(value = "wechat")
public String processMsg(HttpServletRequest request) {
// 调用核心服务类接收处理请求
return weChatService.processRequest(request);
}
}

Service,处理post请求

/**
* 核心服务类
*/
@Service
public class WeChatServiceImpl implements WeChatService{
@Autowired
private FeignUtil feignUtil;
@Autowired
private RedisUtils redisUtils;
public String processRequest(HttpServletRequest request) {
// xml格式的消息数据
String respXml = null;
// 默认返回的文本消息内容
String respContent;
try {
// 调用parseXml方法解析请求消息
Map<String,String> requestMap = WeChatUtil.parseXml(request);
// 消息类型
String msgType = (String) requestMap.get(WeChatContant.MsgType);
String mes = null;
// 文本消息
if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_TEXT)) {
mes =requestMap.get(WeChatContant.Content).toString();
if(mes!=null&&mes.length()<){
List<ArticleItem> items = new ArrayList<>();
ArticleItem item = new ArticleItem();
item.setTitle("照片墙");
item.setDescription("阿狸照片墙");
item.setPicUrl("http://changhaiwx.pagekite.me/photo-wall/a/iali11.jpg");
item.setUrl("http://changhaiwx.pagekite.me/page/photowall");
items.add(item); item = new ArticleItem();
item.setTitle("哈哈");
item.setDescription("一张照片");
item.setPicUrl("http://changhaiwx.pagekite.me/images/me.jpg");
item.setUrl("http://changhaiwx.pagekite.me/page/index");
items.add(item); item = new ArticleItem();
item.setTitle("小游戏2048");
item.setDescription("小游戏2048");
item.setPicUrl("http://changhaiwx.pagekite.me/images/2048.jpg");
item.setUrl("http://changhaiwx.pagekite.me/page/game2048");
items.add(item); item = new ArticleItem();
item.setTitle("百度");
item.setDescription("百度一下");
item.setPicUrl("https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1505100912368&di=69c2ba796aa2afd9a4608e213bf695fb&imgtype=0&src=http%3A%2F%2Ftx.haiqq.com%2Fuploads%2Fallimg%2F170510%2F0634355517-9.jpg");
item.setUrl("http://www.baidu.com");
items.add(item); respXml = WeChatUtil.sendArticleMsg(requestMap, items);
}else if("我的信息".equals(mes)){
Map<String, String> userInfo = getUserInfo(requestMap.get(WeChatContant.FromUserName));
System.out.println(userInfo.toString());
String nickname = userInfo.get("nickname");
String city = userInfo.get("city");
String province = userInfo.get("province");
String country = userInfo.get("country");
String headimgurl = userInfo.get("headimgurl");
List<ArticleItem> items = new ArrayList<>();
ArticleItem item = new ArticleItem();
item.setTitle("你的信息");
item.setDescription("昵称:"+nickname+" 地址:"+country+" "+province+" "+city);
item.setPicUrl(headimgurl);
item.setUrl("http://www.baidu.com");
items.add(item); respXml = WeChatUtil.sendArticleMsg(requestMap, items);
}
}
// 图片消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_IMAGE)) {
respContent = "您发送的是图片消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 语音消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_VOICE)) {
respContent = "您发送的是语音消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 视频消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_VIDEO)) {
respContent = "您发送的是视频消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 地理位置消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_LOCATION)) {
respContent = "您发送的是地理位置消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 链接消息
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_LINK)) {
respContent = "您发送的是链接消息!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 事件推送
else if (msgType.equals(WeChatContant.REQ_MESSAGE_TYPE_EVENT)) {
// 事件类型
String eventType = (String) requestMap.get(WeChatContant.Event);
// 关注
if (eventType.equals(WeChatContant.EVENT_TYPE_SUBSCRIBE)) {
respContent = "谢谢您的关注!";
respXml = WeChatUtil.sendTextMsg(requestMap, respContent);
}
// 取消关注
else if (eventType.equals(WeChatContant.EVENT_TYPE_UNSUBSCRIBE)) {
// TODO 取消订阅后用户不会再收到公众账号发送的消息,因此不需要回复
}
// 扫描带参数二维码
else if (eventType.equals(WeChatContant.EVENT_TYPE_SCAN)) {
// TODO 处理扫描带参数二维码事件
}
// 上报地理位置
else if (eventType.equals(WeChatContant.EVENT_TYPE_LOCATION)) {
// TODO 处理上报地理位置事件
}
// 自定义菜单
else if (eventType.equals(WeChatContant.EVENT_TYPE_CLICK)) {
// TODO 处理菜单点击事件
}
}
mes = mes == null ? "不知道你在干嘛" : mes;
if(respXml == null)
respXml = WeChatUtil.sendTextMsg(requestMap, mes);
System.out.println(respXml);
return respXml;
} catch (Exception e) {
e.printStackTrace();
}
return ""; }
@Override
public String getToken() {
String token = (String) redisUtils.get("token");
if(token == null){
Map<String, String> tokenMap = feignUtil.getToken((String)redisUtils.get(WeChatContant.appID), (String)redisUtils.get(WeChatContant.appsecret));
System.out.println("token:\t"+tokenMap.toString());
token = tokenMap.get("access_token") ;
if(token != null){
redisUtils.set("token", token, 7000L);
} }
return token;
} @Override
public Map<String, String> getUserInfo(String openid) {
// TODO Auto-generated method stub
return feignUtil.getUserInfo(getToken(), openid);
}

}

微信工具类(只写了回复文本消息,和图文消息,其他的都是大同小异)

package com.wechat.util;

import java.io.InputStream;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set; import javax.servlet.http.HttpServletRequest; import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader; import com.wechat.bean.ArticleItem; /**
* 请求校验工具类
*
* @author 32950745
*
*/
public class WeChatUtil { /**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { WeChatContant.TOKEN, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
sort(arr);
StringBuilder content = new StringBuilder();
for (int i = ; i < arr.length; i++) {
content.append(arr[i]);
}
MessageDigest md = null;
String tmpStr = null; try {
md = MessageDigest.getInstance("SHA-1");
// 将三个参数字符串拼接成一个字符串进行sha1加密
byte[] digest = md.digest(content.toString().getBytes());
tmpStr = byteToStr(digest);
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} content = null;
// 将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
} /**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = ; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
} /**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '', '', '', '', '', '', '', '', '', '', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[];
tempArr[] = Digit[(mByte >>> ) & 0X0F];
tempArr[] = Digit[mByte & 0X0F]; String s = new String(tempArr);
return s;
} private static void sort(String a[]) {
for (int i = ; i < a.length - ; i++) {
for (int j = i + ; j < a.length; j++) {
if (a[j].compareTo(a[i]) < ) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
} /**
* 解析微信发来的请求(xml)
*
* @param request
* @return
* @throws Exception
*/
@SuppressWarnings({ "unchecked"})
public static Map<String,String> parseXml(HttpServletRequest request) throws Exception {
// 将解析结果存储在HashMap中
Map<String,String> map = new HashMap<String,String>(); // 从request中取得输入流
InputStream inputStream = request.getInputStream();
// 读取输入流
SAXReader reader = new SAXReader();
Document document = reader.read(inputStream);
// 得到xml根元素
Element root = document.getRootElement();
// 得到根元素的所有子节点
List<Element> elementList = root.elements();
// 遍历所有子节点
for (Element e : elementList)
map.put(e.getName(), e.getText()); // 释放资源
inputStream.close();
inputStream = null;
return map;
} public static String mapToXML(Map map) {
StringBuffer sb = new StringBuffer();
sb.append("<xml>");
mapToXML2(map, sb);
sb.append("</xml>");
try {
return sb.toString();
} catch (Exception e) {
}
return null;
} private static void mapToXML2(Map map, StringBuffer sb) {
Set set = map.keySet();
for (Iterator it = set.iterator(); it.hasNext();) {
String key = (String) it.next();
Object value = map.get(key);
if (null == value)
value = "";
if (value.getClass().getName().equals("java.util.ArrayList")) {
ArrayList list = (ArrayList) map.get(key);
sb.append("<" + key + ">");
for (int i = ; i < list.size(); i++) {
HashMap hm = (HashMap) list.get(i);
mapToXML2(hm, sb);
}
sb.append("</" + key + ">"); } else {
if (value instanceof HashMap) {
sb.append("<" + key + ">");
mapToXML2((HashMap) value, sb);
sb.append("</" + key + ">");
} else {
sb.append("<" + key + "><![CDATA[" + value + "]]></" + key + ">");
} } }
}
/**
* 回复文本消息
* @param requestMap
* @param content
* @return
*/
public static String sendTextMsg(Map<String,String> requestMap,String content){ Map<String,Object> map=new HashMap<String, Object>();
map.put("ToUserName", requestMap.get(WeChatContant.FromUserName));
map.put("FromUserName", requestMap.get(WeChatContant.ToUserName));
map.put("MsgType", WeChatContant.RESP_MESSAGE_TYPE_TEXT);
map.put("CreateTime", new Date().getTime());
map.put("Content", content);
return mapToXML(map);
}
/**
* 回复图文消息
* @param requestMap
* @param items
* @return
*/
public static String sendArticleMsg(Map<String,String> requestMap,List<ArticleItem> items){
if(items == null || items.size()<){
return "";
}
Map<String,Object> map=new HashMap<String, Object>();
map.put("ToUserName", requestMap.get(WeChatContant.FromUserName));
map.put("FromUserName", requestMap.get(WeChatContant.ToUserName));
map.put("MsgType", "news");
map.put("CreateTime", new Date().getTime());
List<Map<String,Object>> Articles=new ArrayList<Map<String,Object>>();
for(ArticleItem itembean : items){
Map<String,Object> item=new HashMap<String, Object>();
Map<String,Object> itemContent=new HashMap<String, Object>();
itemContent.put("Title", itembean.getTitle());
itemContent.put("Description", itembean.getDescription());
itemContent.put("PicUrl", itembean.getPicUrl());
itemContent.put("Url", itembean.getUrl());
item.put("item",itemContent);
Articles.add(item);
}
map.put("Articles", Articles);
map.put("ArticleCount", Articles.size());
return mapToXML(map);
} }

微信常量

public class WeChatContant {
//APPID
public static final String appID = "appid";
//appsecret
public static final String appsecret = "appsecret";
// Token
public static final String TOKEN = "zch";
public static final String RESP_MESSAGE_TYPE_TEXT = "text";
public static final Object REQ_MESSAGE_TYPE_TEXT = "text";
public static final Object REQ_MESSAGE_TYPE_IMAGE = "image";
public static final Object REQ_MESSAGE_TYPE_VOICE = "voice";
public static final Object REQ_MESSAGE_TYPE_VIDEO = "video";
public static final Object REQ_MESSAGE_TYPE_LOCATION = "location";
public static final Object REQ_MESSAGE_TYPE_LINK = "link";
public static final Object REQ_MESSAGE_TYPE_EVENT = "event";
public static final Object EVENT_TYPE_SUBSCRIBE = "SUBSCRIBE";
public static final Object EVENT_TYPE_UNSUBSCRIBE = "UNSUBSCRIBE";
public static final Object EVENT_TYPE_SCAN = "SCAN";
public static final Object EVENT_TYPE_LOCATION = "LOCATION";
public static final Object EVENT_TYPE_CLICK = "CLICK"; public static final String FromUserName = "FromUserName";
public static final String ToUserName = "ToUserName";
public static final String MsgType = "MsgType";
public static final String Content = "Content";
public static final String Event = "Event"; }

图文消息实体bean

public class ArticleItem {
private String Title;
private String Description;
private String PicUrl;
private String Url;
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 getPicUrl() {
return PicUrl;
}
public void setPicUrl(String picUrl) {
PicUrl = picUrl;
}
public String getUrl() {
return Url;
}
public void setUrl(String url) {
Url = url;
} }

好了运行项目,在公众平台配置好url,个人订阅号完成.

项目github https://github.com/zhangchanghai/wechat.git (本项目属于闲暇时 自学的写的 DEMO, 仅用于参考)

基于springboot微信公众号开发,几分钟学会微信自动回复的更多相关文章

  1. 微信公众号开发《三》微信JS-SDK之地理位置的获取,集成百度地图实现在线地图搜索

    本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与 ...

  2. 微信公众号开发《三》微信JS-SDK之地理位置的获取与在线导航,集成百度地图实现在线地图搜索

    本次讲解微信开发第三篇:获取用户地址位置信息,是非常常用的功能,特别是服务行业公众号,尤为需要该功能,本次讲解的就是如何调用微信JS-SDK接口,获取用户位置信息,并结合百度地铁,实现在线地图搜索,与 ...

  3. .NET微信公众号开发-1.0初始微信公众号

    一.前言 微信公众号是开发者或商家在微信公众平台上申请的应用账号,该帐号与QQ账号互通,通过公众号,商家可在微信平台上实现和特定群体的文字.图片.语音.视频的全方位沟通.互动 .形成了一 种主流的线上 ...

  4. 3.微信公众号开发:配置与微信公众平台服务器交互的URL接口地址

    微信开发基本原理: 1.首先有3个对象 分别是微信用户端 微信公众平台服务器 开发者服务器(也就是放自己代码的服务器) 三者间互相交互 2.微信公众平台服务器 充当中间者角色 (以被动回复消息为例) ...

  5. 微信公众号开发--.Net Core实现微信消息加解密

    1:准备工作 进入微信公众号后台设置微信服务器配置参数(注意:Token和EncodingAESKey必须和微信服务器验证参数保持一致,不然验证不会通过). 2:基本配置 设置为安全模式 3.代码实现 ...

  6. 微信公众号开发C#系列-9、多公众号集中管理

    1.概述 通过前面8篇关于微信开发相关文章的学习,我们已经对微信常用开发有了一个比较深入的了解.前面的文章都是基于某一特定公众号的,在现实业务中同一单位个体运营着不至一个公众号,此时就需要对多个公众号 ...

  7. 微信公众号开发(一)--验证服务器地址的Java实现

    现在主流上都用php写微信公众号后台,其实作为后端语言之一的java也可以实现. 这篇文章将对验证服务器地址这一步做出实现. 参考资料:1.慕课网-<初识java微信公众号开发>,2.微信 ...

  8. 微信公众号开发被动回复用户消息,回复内容Content使用了"\n"换行符还是没有换行

    使用语言和框架:本人后端开发使用的Python的DRF(Django REST framework)框架 需求:在微信公众号开发时,需要实现自动回复,即被关注回复.收到消息回复.关键词回复 发现问题: ...

  9. python之微信公众号开发(基本配置和校验)

    前言 最近有微信公众号开发的业务,以前没有用python做过微信公众号开发,记录一下自己的学习和开发历程,共勉! 公众号类型 订阅号 普通订阅号 认证订阅号 服务号 普通服务号 认证服务号 服务方式 ...

随机推荐

  1. Android锁屏状态下弹出activity

    在接收消息广播的onReceive里.跳转到你要显示的界面.如: Intent intent = new Intent(arg0,MainActivity.class); intent.addFlag ...

  2. PHP开发小技巧,让你瞬间提升逼格

    说到PHP代码的优化,PHP开发的小技巧我想很多人都有自己的一套,下面分享一些小技巧,希望对大家有所帮助. 1.循环内部不要声明变量,尤其是对象这样的变量. 2.foreach效率更高,尽量用fore ...

  3. selenium+python安装配置

    一.安装步骤 1.python安装 2.selenium安装     2.1. 方法一:在Windows命令行(cmd)输入pip install selenium即可自动安装selenium,安装完 ...

  4. 超详细的 Linux CentOS yum 源的配置与使用【转发+新增】

    一.yum 简介 yum,是Yellow dog Updater, Modified 的简称,是杜克大学为了提高RPM 软件包安装性而开发的一种软件包管理器.起初是由yellow dog 这一发行版的 ...

  5. rwx读写执行对文件和目录的意义

    文件 目录 r 查看 列出目录内容 w 修改 在目录内新建删除文件 x 执行 可以进入目录 对文件的删除权限是对文件所有目录的写权限 对目录-wx的权限,有写和执行权限,既可以在目录内创建删除文件,可 ...

  6. Error Running Git Empty git --version output:IDEA关联GitHub时出现这个错误

    刚刚学习使用idea中,想要把自己的项目上传到github,遇到这样一个问题,先记录下来,到时候解决了在把方法贴出来. ---------------------------------------- ...

  7. bzoj 4765: 普通计算姬

    Description "奋战三星期,造台计算机".小G响应号召,花了三小时造了台普通计算姬.普通计算姬比普通计算机要厉害一些 .普通计算机能计算数列区间和,而普通计算姬能计算树中 ...

  8. form表单与后台请求的关系

    开发中遇到一个问题,说这个问题前先看一下代码 后台方面, get请求: post请求: 前端方面: 问题是:当我点击提交表单后,页面会跳转成这样: 经过多番测试,原因竟是form表单的提交问题,如果用 ...

  9. tar --打包和压缩

    tar  参考链接 作用:为linux的文件和目录创建档案,也可以在档案中改变文件,或者向档案中加入新的文件即用来压缩和解压文件.tar本身不具有压缩功能.他是调用压缩功能实现的 语法:tar[必要参 ...

  10. Django2.0中文文档

    title: Django2.0中文文档 tags: Python,Django,入沐三分 grammar_cjkRuby: true --- Django2.0版本已经发布了,我们先来看一个图片 从 ...