微信公众号之后台开发

生活中,最使人疲惫的往往不是道路的遥远,而是心中的郁闷;最使人痛苦的往往不是生活的不幸,而是希望的破灭;最使人颓废的往往不是前途的坎坷,而是自信的丧失;最使人绝望的往往不是挫折的打击,而是心灵的死亡。所以我们要有自己的梦想,让梦想的星光指引着我们走出落漠,走出惆怅,带着我们走进自己的理想。

导读:本篇文章讲解 微信公众号之后台开发,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com,来源:原文

一、公众号后台配置

开发第一步,微信服务器要验证服务器是否有效。登录微信公众平台,在开发-基本设置页面,服务器配置项填写: 服务器地址(URL) 、令牌(Token) 、消息加解密密钥(EncodingAESKey)

针对URL开发两个接口:
		GET请求接口:用来做服务器有效性验证
		POST请求接口:用来接收微信服务器发送来的消息。
		
Token可以任意填写,用作生成签名

EncodingAESKey手动填写或随机生成,用作消息体加解密密钥。

在这里插入图片描述
现在提交肯定是验证token失败,因为还需要完成代码逻辑。

在这里插入图片描述

二、使用内网穿透

这里使用NATAPP内网穿透,文档齐全,很简单。

在这里插入图片描述

三、引入依赖

   <!--xml解析-->
        <dependency>
            <groupId>org.dom4j</groupId>
            <artifactId>dom4j</artifactId>
            <version>2.1.3</version>
        </dependency>
        <!--对象转xml-->
        <dependency>
            <groupId>com.thoughtworks.xstream</groupId>
            <artifactId>xstream</artifactId>
            <version>1.4.10</version>
        </dependency>

四、服务器校验

校验逻辑流程

在这里插入图片描述

校验逻辑实现

@RestController
@RequestMapping("/WeChat")
public class WxController {
    
    //与微信公众号 开发 基本配置中配置token保持一致
    private static final String TOKEN = "token";

    private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};

    /**
     * 服务器校验
     * 安全校验,标识请求是否来源于微信
     *
     * @param request
     * @param response
     * @throws UnsupportedEncodingException
     */
    @GetMapping("/wx")
    public void verifyToken(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException {
        request.setCharacterEncoding("UTF-8");
        //微信加密签名
        String signature = request.getParameter("signature");
        //时间戳
        String timestamp = request.getParameter("timestamp");
        //随机数
        String nonce = request.getParameter("nonce");
        //随机字符串
        String echostr = request.getParameter("echostr");
        PrintWriter out = null;
        try {
            out = response.getWriter();
            if (checkSignature(signature, timestamp, nonce)) {
                out.write(echostr);
            }
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            out.close();
        }
    }


    /**
     * 校验微信签名
     *
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    private static boolean checkSignature(String signature, String timestamp, String nonce) {
        String[] str = new String[]{TOKEN, timestamp, nonce};
        //对token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(str);
        //将三个参数字符串拼接成一个字符串进行sha1加密
        StringBuffer buffer = new StringBuffer();
        for (int i = 0; i < str.length; i++) {
            buffer.append(str[i]);
        }
        String temp = encode(buffer.toString());
        //获得加密后的字符串与signature对比
        return signature.equals(temp);
    }


    /**
     * 对字符串进行加密
     *
     * @param str
     * @return
     */
    private static String encode(String str) {
        if (str == null) {
            return null;
        }
        MessageDigest messageDigest = null;
        try {
            messageDigest = MessageDigest.getInstance("SHA1");
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        messageDigest.update(str.getBytes());
        byte[] digest = messageDigest.digest();

        int len = digest.length;
        StringBuilder buf = new StringBuilder(len * 2);
        for (int j = 0; j < len; j++) {
            buf.append(HEX_DIGITS[(digest[j] >> 4) & 0x0f]);
            buf.append(HEX_DIGITS[digest[j] & 0x0f]);
        }
        return buf.toString();
    }
}

在这里插入图片描述

五、消息接收

消息接收接口和服务器校验接口地址是一样的,只不过消息接收接口是一个 POST 请求。由于公众号后台配置消息加解密方式:明文模式,在后台收到的消息可以直接处理。

接收逻辑流程

1.收到微信服务器发来的消息之后,进行XML解析,提取出需要的信息。

2.根据发送的消息类型或业务做相关操作,再将操作的结果返回给微信服务器。

在这里插入图片描述

接收与回复文本消息格式

1.接受到的文本消息格式

<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位整型

2.回复文本消息格式

收到粉丝消息后不想或者不能5秒内回复时,需回复“success”字符串

<xml>
 <ToUserName><![CDATA[粉丝号]]></ToUserName>
 <FromUserName><![CDATA[公众号]]></FromUserName>
 <CreateTime>1460541339</CreateTime>
 <MsgType><![CDATA[text]]></MsgType>
 <Content><![CDATA[test]]></Content>
</xml>
参数 是否必须 描述
ToUserName 接收方帐号(收到的OpenID)
FromUserName 开发者微信号
CreateTime 消息创建时间 (整型)
MsgType 消息类型,文本为text
Content 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示)

接收与回复逻辑实现

 /**
     * 接受微信发送的消息
     *
     * @param request
     * @param response
     * @throws Exception
     */
    @PostMapping("/wx")
    public void processTheMessage(HttpServletRequest request, HttpServletResponse response) throws Exception {
        Map<String, String> parseXml = parseXml(request);
        /**
         * 接受到的文本消息格式
         * <xml>
         *  <ToUserName><![CDATA[公众号]]></ToUserName>
         *  <FromUserName><![CDATA[粉丝号]]></FromUserName>
         *  <CreateTime>1460537339</CreateTime>
         *  <MsgType><![CDATA[text]]></MsgType>
         *  <Content><![CDATA[欢迎开启公众号开发者模式]]></Content>
         *  <MsgId>6272960105994287618</MsgId>
         * </xml>
         */
        //开发者微信号
        String tousername = parseXml.get("ToUserName");
        //发送方帐号(一个OpenID)
        String fromusername = parseXml.get("FromUserName");
        //消息创建时间 (整型)
        String createTime = parseXml.get("CreateTime");
        //消息类型,文本为text
        String msgType = parseXml.get("MsgType");
        //文本消息内容
        String content = parseXml.get("Content");
        //消息id,64位整型
        String msgId = parseXml.get("MsgId");

        System.out.println("开发者微信号: " + tousername);
        System.out.println("发送方帐号: " + fromusername);
        System.out.println("消息创建时间: " + createTime);
        System.out.println("消息类型: " + msgType);
        System.out.println("文本消息内容: " + content);
        System.out.println("消息id: " + msgId);

        /**
         * 被动回复文本消息的格式
         *  <xml>
         *  <ToUserName><![CDATA[粉丝号]]></ToUserName>
         *  <FromUserName><![CDATA[公众号]]></FromUserName>
         *  <CreateTime>1460541339</CreateTime>
         *  <MsgType><![CDATA[text]]></MsgType>
         *  <Content><![CDATA[test]]></Content>
         * </xml>
         *
         * 需注意ToUserName与FromUserName的设值
         */
        Message message = new Message();
        message.setToUserName(fromusername);
        message.setFromUserName(tousername);
        message.setCreateTime(createTime);
        message.setMsgType(msgType);
        message.setContent("Hello  管理员。");

        WxController.xstream.alias("xml", Message.class);
        String xml = WxController.xstream.toXML(message);
        System.out.println(xml);
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        out.println(xml);
        out.flush();
    }

    /**
     * 解析微信发来的请求(XML)
     *
     * @param request
     * @return
     * @throws Exception
     */
    private 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;
    }

    /**
     * 扩展xstream使其支持CDATA
     */
    private static XStream xstream = new XStream(new XppDriver() {
        @Override
        public HierarchicalStreamWriter createWriter(Writer out) {
            return new PrettyPrintWriter(out) {
                // 对所有xml节点的转换都增加CDATA标记
                boolean cdata = true;

                @Override
                public void startNode(String name, Class clazz) {
                    super.startNode(name, clazz);
                }

                @Override
                protected void writeText(QuickWriter writer, String text) {
                    if (cdata) {
                        writer.write("<![CDATA[");
                        writer.write(text);
                        writer.write("]]>");
                    } else {
                        writer.write(text);
                    }
                }
            };
        }
    });

六、 执行测试

在这里插入图片描述

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/137093.html

(1)
飞熊的头像飞熊bm

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!