基于springboot微信公众号开发,几分钟学会微信自动回复

效果图

1.准备工作

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

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

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

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

2.服务器代码

创建个springboot工程

pom.xml

<properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</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.9.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.22</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()<2){
                    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 "";

    }
}

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

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 = 0; 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 = 0; i < byteArray.length; i++) {
            strDigest += byteToHexStr(byteArray[i]);
        }
        return strDigest;
    }

    /**
     * 将字节转换为十六进制字符串
     *
     * @param mByte
     * @return
     */
    private static String byteToHexStr(byte mByte) {
        char[] Digit = { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘A‘, ‘B‘, ‘C‘, ‘D‘, ‘E‘, ‘F‘ };
        char[] tempArr = new char[2];
        tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
        tempArr[1] = Digit[mByte & 0X0F];

        String s = new String(tempArr);
        return s;
    }

    private static void sort(String a[]) {
        for (int i = 0; i < a.length - 1; i++) {
            for (int j = i + 1; j < a.length; j++) {
                if (a[j].compareTo(a[i]) < 0) {
                    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 = 0; 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()<1){
            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,个人订阅号完成.

原文地址:https://www.cnblogs.com/changhai/p/8289808.html

时间: 2024-10-06 11:15:13

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

微信公众号开发纪要(4)-调用微信扫一扫功能

在微信公众号页面中调用微信扫一扫功能,就是调用微信JS-SDK.让JS-SDK完成调用摄像头扫描,然后我们将扫描结果进行业务操作.微信JS-SDK是微信公众平台面向网页开发者提供的基于微信内的网页开发工具包.通过使用微信JS-SDK,网页开发者可借助微信高效地使用拍照.选图.语音.位置等手机系统的能力,同时可以直接使用微信分享.扫一扫.卡券.支付等微信特有的能力,为微信用户提供更优质的网页体验.如何调用微信JS-SDK,在微信公众平台技术文档中的微信网页开发-->微信JS-SDK说明文档中有详细

微信公众号开发_如何接入微信支付?[网页发起支付]

开发背景 在微信公众号中,需要获取支付功能 前提 公众号已经开通支付功能[未开通的自行百度] 实现步骤 接入JSSDK JSSDK使用步骤 参考微信JS-SDK说明文档 使用JSSDK中的 微信支付 API wx.chooseWXPay({ timestamp: 0, // 支付签名时间戳,注意微信jssdk中的所有使用timestamp字段均为小写.但最新版的支付后台生成签名使用的timeStamp字段名需大写其中的S字符 nonceStr: 'abc', // 支付签名随机串,不长于 32

微信公众号开发(一)微信验证开发者服务器接口

微信验证开发者服务器接口 微信验证开发者服务器接口 如图所示,开发者可填写自己服务器的验证token的接口地址,以及自定义的token(博主申请的测试号,使用natapp来进行内网穿透) 目的:帮助微信服务器和开发者服务器互相识别,以防恶意攻击 流程图如下(不知道博客园怎么显示md流程图,有知道的仁兄告知):(取自微信公众平台技术文档) st=>start: 开启服务 ipop1=>inputoutput: 接收到数据[不确定是谁发来的] op1=>operation: 尝试提取出sig

微信公众号开发技术基础(二):外网映射工具ngrok的简介和使用

本文结构:(一)用法(二)示例(三)qydev ngrok的下载链接及相关网站 微信公众号开发的时候,微信服务器是需要访问我们的一个公网服务器地址的,但我们又是在本地调试自己的程序的,那怎样让微信服务器能访问到我们本地的服务器呢?那就需要用外网映射工具,将本地IP映射成公网IP,这样就能在公网上访问本地服务了,这里使用ngrok.ngrok原版程序的服务器是在国外的,访问速度极慢或者干脆访问不了,所以这里提供一个服务器搭建在国内的基于ngrok的一个软件:qydev,百度网盘下载链接见文章结尾.

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

1.概述 通过前面8篇关于微信开发相关文章的学习,我们已经对微信常用开发有了一个比较深入的了解.前面的文章都是基于某一特定公众号的,在现实业务中同一单位个体运营着不至一个公众号,此时就需要对多个公众号集中管理,随意切换.本篇文章主要介绍多公众号集中管理的方法.表设计.设置默认公众号.生成指定格式的URL资源服务器.刷新Access_Token等. 2.公众号集中管理的方法 我们知道操作微信公众号时微信服务器都会返回相应的信息到我们自己的中转服务器上,涉及多个微信公众号管理时,我们就需要知道是那个

微信公众号开发系列导航

 1.0.NET微信公众号开发-1.0初始微信公众号 1.0.NET微信公众号开发-1.0初始微信公众号 2.0.NET微信公众号开发-2.0创建自定义菜单  2.0.NET微信公众号开发-2.0创建自定义菜单 3.0.NET微信公众号开发-3.0查询自定义菜单  3.0.NET微信公众号开发-3.0查询自定义菜单 4.0.NET微信公众号开发-4.0公众号消息处理  4.0.NET微信公众号开发-4.0公众号消息处理 5.0.NET微信公众号开发-5.0微信支付  5.0.NET微信公众号开发

微信公众号开发入门心得(一)

一.前言 为了快速了解微信公众号开发过程,我选择了php语言来进行微信公众号开发,选择php的原因有两点,一是网上有很多用php开放微信公众号的教程,可以减少学习成本:二是php也比较容易学习,学习门槛低. 学习php开发微信公众号,开发者需要有一点php基础.对于学过其他编程语言的童鞋,了解一些基本的php语法就可以开始了.我的 PHP基础学习笔记(一) 二.微信公众号开发的步骤 三.微信公众号开发步骤详解   + 微信公众号开发入门可以参考 微信公众平台开发入门教程 教程中用的新浪云做的服务

带领技术小白入门——基于java的微信公众号开发(包括服务器配置、java web项目搭建、tomcat手动发布web项目、微信开发所需的url和token验证)

微信公众号对于每个人来说都不陌生,但是许多人都不清楚是怎么开发的.身为技术小白的我,在闲暇之余研究了一下基于java的微信公众号开发.下面就是我的实现步骤,写的略显粗糙,希望大家多多提议! 一.申请服务器 1.我购买的是阿里云服务器,购买后要设置一下服务器密码,默认用户名是administrator,购买好后如下: 2.申请好后,copy一下此服务器的IP地址(公有),在本地ping一下看看是否可用,j键盘Win+R,输入cmd,输入ping+IP回车,如下即为成功: 二.配置服务器 1.下载远

C#微信公众号开发 -- (五)自定义菜单创建

公众号中,底部都是有自己定义的功能按钮,通过点击某个按钮来实现指定的业务逻辑操作. 下面就来说说这些按钮是怎样放到微信公众平台的,还是先来看看微信的官方解释: 请注意: 1.自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单. 2.一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以“...”代替. 3.创建自定义菜单后,菜单的刷新策略是,在用户进入公众号会话页或公众号profile页时,如果发现上一次拉取菜单的请求在5分钟以前,就会拉取一下菜单, 如果菜单有更新,就会刷