公众号后台开发

1.准备

1.准备服务

与微信对接的url要具备以下条件:

(1)在公网上能够访问

(2)端口只支持80端口

  在这里如果是公网能够访问的服务最好,也可以通过花生壳或者其他外网映射工具进行映射,比如ngrok

2.数据交互原理

  开发模式与编辑模式是互斥的,打开开发模式的时候,编辑模式的自动回复与自定义菜单失效;打开编辑模式的自动回复或者自定义菜单的时候开发模式会失效。

开发模式的数据交互原理如下:

  我们需要开发的任务就是维信公众号服务器,包括业务逻辑、身份验证等操作。

2.接入后台

  参考公众号开发文档:   开发->开发者工具-》开发者文档,里面有类似于对接钉钉的文档,有接入指南以及其他接口文档。

  https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Overview.html

1.  第一步:填写服务器配置

2 第二步:验证消息的确来自微信服务器(在自己的微信服务器进行验证)

  开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带参数如下表所示:

开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。加密/校验流程如下:

1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

SpringMVC接收代码如下:

package cn.qlq.controller.weixin;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

import cn.qlq.controller.UserController;
import cn.qlq.utils.weixin.WeixinCheckUtils;

@Controller
@RequestMapping("weixin")
public class WeixinController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @ResponseBody
    @RequestMapping("index")
    public String index(@RequestParam(required = false) String signature,
            @RequestParam(required = false) String timestamp, @RequestParam(required = false) String nonce,
            @RequestParam(required = false) String echostr) {

        logger.debug("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr);

        if (StringUtils.isNoneBlank(signature, timestamp, nonce)
                && WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) {
            return echostr;
        }

        return "error";
    }
}

验证工具如下:

package cn.qlq.utils.weixin;

import java.security.MessageDigest;
import java.util.Arrays;

public class WeixinCheckUtils {

    // token,与微信公众号后台的一致
    private static final String token = "devqiaolq";

    public static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] arr = new String[] { token, timestamp, nonce };

        // 排序
        Arrays.sort(arr);

        // 生成字符串
        StringBuffer content = new StringBuffer();
        for (int i = 0; i < arr.length; i++) {
            content.append(arr[i]);
        }

        // sha1加密
        String temp = getSha1(content.toString());
        return temp.equals(signature);

    }

    public static String getSha1(String str) {
        if (str == null || str.length() == 0) {
            return null;
        }

        char hexDigits[] = { ‘0‘, ‘1‘, ‘2‘, ‘3‘, ‘4‘, ‘5‘, ‘6‘, ‘7‘, ‘8‘, ‘9‘, ‘a‘, ‘b‘, ‘c‘, ‘d‘, ‘e‘, ‘f‘ };

        try {
            MessageDigest mdTemp = MessageDigest.getInstance("SHA1");
            mdTemp.update(str.getBytes("UTF-8"));

            byte[] md = mdTemp.digest();
            int j = md.length;
            char buf[] = new char[j * 2];
            int k = 0;
            for (int i = 0; i < j; i++) {
                byte byte0 = md[i];
                buf[k++] = hexDigits[byte0 >>> 4 & 0xf];
                buf[k++] = hexDigits[byte0 & 0xf];
            }
            return new String(buf);
        } catch (Exception e) {
            return null;
        }
    }
}

  注意:如果有登录过滤器,记得在过滤器中放行微信请求.

3. 第三步:依据接口文档实现业务逻辑

1. 首先需要启用开发者模式:(启用开发者模式之后自己的自定义菜单就不会生效)

2. 接收与响应文字消息

  当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。

文本消息的xml数据格式如下:

<xml>
  <ToUserName><![CDATA[toUser]]></ToUserName>
  <FromUserName><![CDATA[fromUser]]></FromUserName>
  <CreateTime>1348831860</CreateTime>
  <MsgType><![CDATA[text]]></MsgType>
  <Content><![CDATA[this is a test]]></Content>
  <MsgId>1234567890123456</MsgId>
</xml>

参数解释:

参数 描述
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType 消息类型,文本为text
Content 文本消息内容
MsgId 消息id,64位整型

(1)建立后台对应的TextMessage实体类:

package cn.qlq.bean.weixin;

public class TextMessage {

    /**
     * 开发者微信号
     */
    private String ToUserName;

    /**
     * 发送方帐号(一个OpenID)
     */
    private String FromUserName;

    /**
     * 消息创建时间 (整型)
     */
    private long CreateTime;

    /**
     * text
     */
    private String MsgType;

    /**
     * 文本消息内容
     */
    private String Content;

    /**
     * 消息id,64位整型
     */
    private String MsgId;

    @Override
    public String toString() {
        return "TextMessage{" + "ToUserName=‘" + ToUserName + ‘\‘‘ + ", FromUserName=‘" + FromUserName + ‘\‘‘
                + ", CreateTime=" + CreateTime + ", MsgType=‘" + MsgType + ‘\‘‘ + ", Content=‘" + Content + ‘\‘‘
                + ", MsgId=‘" + MsgId + ‘\‘‘ + ‘}‘;
    }

    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 String getContent() {
        return Content;
    }

    public void setContent(String content) {
        Content = content;
    }

    public String getMsgId() {
        return MsgId;
    }

    public void setMsgId(String msgId) {
        MsgId = msgId;
    }

}

(2)编写工具类实现xml(接收的是xml格式的数据)转map和TextMessage对象转换成xml(响应数据格式为xml)

pom加入:

        <!--微信转换XML所需包 -->
        <dependency>
            <groupId>dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>1.6.1</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.thoughtworks.xstream/xstream -->
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.10</version>
        </dependency>

工具类:

package cn.qlq.utils.weixin;

import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.servlet.http.HttpServletRequest;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.thoughtworks.xstream.XStream;

import cn.qlq.bean.weixin.TextMessage;

public class MessageUtils {

    /**
     * xml数据转map
     *
     * @param request
     * @return
     * @throws IOException
     * @throws DocumentException
     */
    public static Map<String, String> xmlToMap(HttpServletRequest request) throws IOException, DocumentException {
        Map<String, String> map = new HashMap<>();
        SAXReader reader = new SAXReader();

        InputStream inputStream = request.getInputStream();
        Document document = reader.read(inputStream);

        Element root = document.getRootElement();
        List<Element> list = root.elements();

        for (Element element : list) {
            map.put(element.getName(), element.getText());
        }

        inputStream.close();
        return map;
    }

    /**
     * 将文本消息对象转换成xml
     *
     * @param textMessage
     * @return
     */
    public static String textMessageToXml(TextMessage textMessage) {
        XStream xStream = new XStream();
        // 将xml的根元素替换成xml
        xStream.alias("xml", textMessage.getClass());
        return xStream.toXML(textMessage);
    }
}

(3)重写Controller接收消息和响应消息

package cn.qlq.controller.weixin;

import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Map;

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

import org.apache.commons.lang3.StringUtils;
import org.dom4j.DocumentException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import cn.qlq.bean.weixin.TextMessage;
import cn.qlq.controller.UserController;
import cn.qlq.utils.weixin.MessageUtils;
import cn.qlq.utils.weixin.WeixinCheckUtils;

@Controller
@RequestMapping("weixin")
public class WeixinController {

    private static final Logger logger = LoggerFactory.getLogger(UserController.class);

    @RequestMapping(value = "index", method = { RequestMethod.GET, RequestMethod.POST })
    public void index(HttpServletRequest request, HttpServletResponse response) throws IOException {

        // 将请求、响应的编码均设置为UTF-8(防止中文乱码)
        request.setCharacterEncoding("UTF-8"); // 微信服务器POST消息时用的是UTF-8编码,在接收时也要用同样的编码,否则中文会乱码;
        response.setCharacterEncoding("UTF-8"); // 在响应消息(回复消息给用户)时,也将编码方式设置为UTF-8,原理同上;

        String method = request.getMethod().toLowerCase();
        logger.info("method: {}", method);

        // 验证是否是微信请求
        if ("get".equals(method)) {
            doGet(request, response);
            return;
        }

        // POST请求接收消息,且给客户响应消息
        doPost(request, response);
    }

    /**
     * Post请求用于接收消息且处理消息之后回传消息
     *
     * @param request
     * @param response
     * @throws IOException
     */
    private void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException {
        PrintWriter out = response.getWriter();
        try {
            Map<String, String> map = MessageUtils.xmlToMap(request);
            String fromUserName = map.get("FromUserName");
            String toUserName = map.get("ToUserName");
            String msgType = map.get("MsgType");
            String content = map.get("Content");
            logger.info("map: {}", map);

            if (StringUtils.isNotBlank(content)) {
                System.out.println("接收的的消息为:" + content + ",你可以根据关键字进行搜索或者做其他");
            }

            String message = null;
            if ("text".equals(msgType)) {
                TextMessage textMessage = new TextMessage();
                // 回传消息,所以讲fromuser和toUser交换
                textMessage.setFromUserName(toUserName);
                textMessage.setToUserName(fromUserName);
                textMessage.setMsgType(msgType);
                textMessage.setCreateTime(new Date().getTime());
                textMessage.setContent("您发送的消息为: " + content);
                logger.info("textMessage: {}", textMessage);

                message = MessageUtils.textMessageToXml(textMessage);
            }

            out.print(message);// 把消息发送到客户端
        } catch (DocumentException e) {
            logger.error("dispose post request error", e);
        } finally {
            out.close();
        }
    }

    /**
     * Get请求用于微信配置验证
     *
     * @param request
     * @param response
     * @throws IOException
     */
    private void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException {
        String signature = request.getParameter("signature");// 微信加密签名
        String timestamp = request.getParameter("timestamp");// 时间戳
        String nonce = request.getParameter("nonce");// 随机数
        String echostr = request.getParameter("echostr");// 随机字符串
        logger.info("signature: {}, timestamp: {}, nonce: {}, echostr: {}", signature, timestamp, nonce, echostr);

        if (StringUtils.isNoneBlank(signature, timestamp, nonce)
                && WeixinCheckUtils.checkSignature(signature, timestamp, nonce)) {
            response.getWriter().write(echostr);
        }
    }
}

测试:

Java服务器日志如下:

2019-10-23 23:34:15.625 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : method: post
2019-10-23 23:34:15.633 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : map: {MsgId=22503405793257008, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844638, Content=怎么了, ToUserName=gh_fc4bd5c2fda8, MsgType=text}
接收的的消息为:怎么了,你可以根据关键字进行搜索或者做其他
2019-10-23 23:34:15.635 INFO 244500 --- [nio-8088-exec-9] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName=‘o_qAo0u6Snhoc7Z45RfSxYatMWpo‘, FromUserName=‘gh_fc4bd5c2fda8‘, CreateTime=1571844855635, MsgType=‘text‘, Content=‘您发送的消息为: 怎么了‘, MsgId=‘null‘}
2019-10-23 23:34:20.288 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : method: post
2019-10-23 23:34:20.295 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : map: {MsgId=22503406289173072, FromUserName=o_qAo0u6Snhoc7Z45RfSxYatMWpo, CreateTime=1571844642, Content=什么意思, ToUserName=gh_fc4bd5c2fda8, MsgType=text}
接收的的消息为:什么意思,你可以根据关键字进行搜索或者做其他
2019-10-23 23:34:20.296 INFO 244500 --- [nio-8088-exec-3] cn.qlq.controller.UserController : textMessage: TextMessage{ToUserName=‘o_qAo0u6Snhoc7Z45RfSxYatMWpo‘, FromUserName=‘gh_fc4bd5c2fda8‘, CreateTime=1571844860296, MsgType=‘text‘, Content=‘您发送的消息为: 什么意思‘, MsgId=‘null‘}

原文地址:https://www.cnblogs.com/qlqwjy/p/11688870.html

时间: 2024-08-02 15:05:32

公众号后台开发的相关文章

Python微信公众号后台开发&lt;003&gt;:自定义菜单

有同学问道微信公众号后台开发的自定义菜单怎么实现? 这个问题本来想放到后面的,因为的确对公众号的影响挺明显的, 因为开启后台服务,公众号的自定义菜单就不见了,很影响使用, 也有同学问这个问题,就提前了,后面如果有进展会进行更新. 开发文档: https://developers.weixin.qq.com/doc/offiaccount/Getting_Started/Explanation_of_interface_privileges.html 订阅号账号分类及权限 订阅号分为个人号和企业订

PHP微信公众号后台开发(Yii2实现)

本文内容较多,包括微信接入.获取微信用户信息.微信支付.JSSDK配置参数获取等部分.如果读者对微信开发没有一个主观上的认识,那么建议读者先研读微信公众平台开发者文档,然后再阅读本文,效果更佳! 微信开发的完整例子已经整理在Github,欢迎查看: yii2-wechat-demo.[八宝粥的博客] 接入微信 Yii2后台配置 1.在app/config/params.php中配置token参数 return [ //微信接入 'wechat' =>[ 'token' => 'your tok

Python微信公众号后台开发&lt;005&gt;:集成智能聊天机器人?

?给公众号集成一个智能聊天机器人 一.前述 ChatterBot是一个基于机器学习的聊天机器人引擎,构建在python上,主要特点是可以自可以从已有的对话中进行学(jiyi)习(pipei). 二.具体 1.安装 是的,安装超级简单,用pip就可以啦 pip install chatterbot 2.流程 大家已经知道chatterbot的聊天逻辑和输入输出以及存储,是由各种adapter来限定的,我们先看看流程图,一会再一起看点例子,看看怎么用. 3.每个部分都设计了不同的“适配器”(Adap

Spring Boot 开发微信公众号后台

Hello 各位小伙伴,松哥今天要和大家聊一个有意思的话题,就是使用 Spring Boot 开发微信公众号后台. 很多小伙伴可能注意到松哥的个人网站(http://www.javaboy.org)前一阵子上线了一个公众号内回复口令解锁网站文章的功能,还有之前就有的公众号内回复口令获取超 2TB 免费视频教程的功能(免费视频教程),这两个都是松哥基于 Spring Boot 来做的,最近松哥打算通过一个系列的文章,来向小伙伴们介绍下如何通过 Spring Boot 来开发公众号后台. 1. 缘起

微信公众号退款开发

博主是小菜鸟,这篇文章仅是自己开发的随笔记录,不足博友可以指出来,一起进步 1.[微信支付]公众号支付开发者文档链接地址 https://pay.weixin.qq.com/wiki/doc/api/jsapi.php?chapter=9_4 调用微信退款接口,需要发送特定的xml格式字符串到到微信退款接口: 2.微信申请退款需要双向证书, JAVA只需要使用apiclient_cert.p12即可,证书从 https://pay.weixin.qq.com/index.php/core/hom

微信公众号支付开发全过程 --JAVA

按照惯例,开头总得写点感想 ------------------------------------------------------------------ 业务流程 这个微信官网说的还是很详细的,还配了图.我还要再说一遍. 用户点击一个支付按钮-->{后台一大推处理}-->用户看到了一个输入密码的界面,包含金额等一些信息-->用户输入密码后出来一个支付成功的页面(这部分流程都是微信自己完成的,我们什么都不用做)-->返回系统自己的页面(总不能让用户一直看着一个支付完成的页面吧

抛砖微信公众号应用开发

现在微信营销,真是炙手可热,各行各业都在利用微信这个社交平台,基于微信服务器这个服务网关,开发自己的应用服务,通过微信进行传播,真是方便快捷高效!大体的通信架构如下图所示. 由于工作的需要,我也着手研究微信公众平台的开发,在这过去的一周的时间,折腾了一个小实验环境,打通微信的两个基本验证关卡. 第一道验证,就是URL,Token填写完成后,点击提交的过程.(后面会详述) 第二道验证,就是公众号服务器上面的帖子进行分享等操作时,为了让别人看到友好的“标题”+“描述”+“缩略图”这种模式的帖子,必须

使用开源库MAGICODES.WECHAT.SDK进行微信公众号支付开发

概要 博客使用Word发博,发布后,排版会出现很多问题,敬请谅解.可加群获取原始文档. 本篇主要讲解微信支付的开发流程,相关业务基于MAGICODES.WECHAT.SDK实现.通过本篇教程,您可以很方便的快速完成微信公众号支付的开发. 关于Magicodes.WeChat.SDK MAGICODES.WECHAT.SDK为心莱团队封装的轻量级微信SDK,现已全部开源,开源库地址为:https://github.com/xin-lai/Magicodes.WeChat.SDK 更多介绍,请关注后

对柳峰博主的微信公众号后台示例的部分重构

柳峰博主的专栏(http://blog.csdn.net/column/details/wechatmp.html)和王信平博主的专栏(http://www.cnblogs.com/wangshuo1/)对微信公众号开发已经做了比较详尽的阐述,基本上照搬,就可以做出第一个微信公众号后台应用.但是在『照搬』的过程中,发现有些地方总是觉得别扭,由着完美主义者的性格使然,对以下这几个地方做个小优化吧. 一.区别消息和响应 原来的消息类(用户发给后台)和响应类(后台回给用户)有这些: 我改成了这样的: