springBoot 实现微信公众号消息自动回复

准备

  • 注册微信公众号:微信公众平台;或者使用申请测试账号来演示:测试账号申请地址
  • 能够使用外网访问的地址:这里使用的是内网穿透工具花生壳,点击跳转

配置接口

提示:等接口写好后在进行接口配置;配置保存时,微信官方会发送请求到配置的接口进行验证,只有通过验证才能配置成功;

springBoot 实现微信公众号消息自动回复_第1张图片

代码实现

依赖


<dependency>
    <groupId>org.dom4jgroupId>
    <artifactId>dom4jartifactId>
    <version>2.0.0version>
dependency>

编写认证接口,同时也是接收消息接口(GET/POST)

package com.andon.wxPush.web;

import com.alibaba.fastjson.JSONObject;
import com.andon.entity.an.AnCurrentAndon;
import com.andon.wxPush.service.WxReceiveMsg;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.Map;

@Slf4j
@Api(description = "微信公众号接口")
@RestController
@RequestMapping("/wx/officialAccount")
public class WxGzhController {

    @Resource
    private WxReceiveMsg wxReceiveMsg;

    /**
     * 认证接收微信公众号的消息(GET请求进行认证/POST请求进行接收消息并返回)
     */
    @RequestMapping("/signReceiveWxGzhMsg")
    public String checkValid(String signature, String timestamp, String nonce, String echostr, HttpServletRequest req) {
        String requestMethod = req.getMethod();
        if (requestMethod.equals("POST")) { // 处理 POST 请求
            Map<String, String> msgMap = wxReceiveMsg.parseXmlData2Map(req);
            System.out.println(msgMap);
            String s = wxReceiveMsg.receiveMessage(msgMap);
            return s;
        } else if (requestMethod.equals("GET")) { // 处理 GET 请求
            log.info("signature: {} nonce: {} echostr: {} timestamp: {}", signature, nonce, echostr, timestamp);
            return wxReceiveMsg.checkSignature(signature, timestamp, nonce) ? echostr : "校验失败";
        } else {
            return "不是 GET 和 POST";
        }
    }
}

WxReceiveMsg

package com.andon.wxPush.service;

import javax.servlet.http.HttpServletRequest;
import java.util.Map;

public interface WxReceiveMsg {

    boolean checkSignature(String signature, String timestamp, String nonce);

    Map<String, String> parseXmlData2Map(HttpServletRequest req);

    String receiveMessage(Map<String, String> param);
}

WxReceiveMsgImpl

package com.andon.wxPush.service.impl;

import com.andon.wxPush.service.WxReceiveMsg;
import com.andon.wxPush.utils.AesException;
import com.andon.wxPush.utils.SHA1;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import org.springframework.stereotype.Service;

import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.*;

@Service
public class WxReceiveMsgImpl implements WxReceiveMsg {

    private static final String TOKEN = "Eric";

    /**
     * 验证签名
     * @param signature
     * @param timestamp
     * @param nonce
     * @return
     */
    @Override
    public boolean checkSignature(String signature, String timestamp, String nonce) {

        //进行sha1加密
        String temp = null;
        try {
            temp = SHA1.getSHA1(TOKEN, timestamp, nonce);
        } catch (Exception e) {
            e.printStackTrace();
        }
        //与微信提供的signature进行匹对
        return signature.equals(temp);
    }

    /**
     * 解析xml格式数据转为map格式
     * @param req
     * @return
     */
    @Override
    public Map<String, String> parseXmlData2Map(HttpServletRequest req) {
        HashMap<String, String> msgMap = new HashMap<>();

        try {
            ServletInputStream inputStream = req.getInputStream();

            // dom4j 用于读取 XML 文件输入流的类
            SAXReader saxReader = new SAXReader();
            // 读取 XML 文件输入流, XML 文档对象
            Document document = saxReader.read(inputStream);
            // XML 文件的根节点
            Element root = document.getRootElement();
            // 所有的子节点
            List<Element> childrenElement = root.elements();
            for (Element element : childrenElement) {
                msgMap.put(element.getName(), element.getStringValue());
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return msgMap;
    }

    /**
     * 接收消息,判断消息类型,并根据关键词进行回复
     * @param param
     * @return
     */
    @Override
    public String receiveMessage(Map<String, String> param) {
        String content = "";
        try {
            //消息类型
            String msgType = param.get("MsgType");
            switch(msgType){
                case "text" :		//普通文本类型,例如用户发送:java
                    content = this.keyWordMsgTip(param);
                    break;
                default:
                    content = "success";
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return content;
    }

    /**
     * 处理关键字搜索事件
     * 图文消息个数;当用户发送文本、图片、语音、视频、图文、地理位置这六种消息时,开发者只能回复1条图文消息;其余场景最多可回复8条图文消息
     * @param param
     * @return
     */
    private String keyWordMsgTip(Map<String, String> param) {
        String fromusername = param.get("FromUserName");
        String tousername = param.get("ToUserName");
        String content = param.get("Content");

        //单位为秒,不是毫秒
        Long createTime = new Date().getTime() / 1000;
        StringBuffer text = new StringBuffer();
        //这个判断是为靓仔专属设定的
        if("java".equals(content)){
            //一次只能返回一个
            //标题
            String title = "Java";
            //描述
            String description = "java详情";
            //图片
            String PicUrl = "https://amaxchina.picp.vip/system/api/resourceFile/1679982783397_1_UserAvatar_cropped.png";
            //链接地址
            String url = null;
            try {
                String redirect_uri = URLEncoder.encode("https://amaxchina.picp.vip/#/authentication", "UTF-8");
                url = "https://java.com/";
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }

            StringBuffer articles = new StringBuffer();
            articles.append("");
            articles.append("<![CDATA["</span><span class="token operator">+</span>title<span class="token operator">+</span><span class="token string">"]]>");
            articles.append("+description+"]]>");
            articles.append("+PicUrl+"]]>");
            articles.append("+url+"]]>");
            articles.append("");

            text.append("");
            text.append("+fromusername+"]]>");
            text.append("+tousername+"]]>");
            text.append("+createTime+"]]>");
            text.append("");
            text.append("");
            text.append("");
            text.append(articles);
            text.append("");
            text.append("");
        }

        return text.toString();
    }
}

SHA1

package com.andon.wxPush.utils;

/**
 * 对公众平台发送给公众账号的消息加解密示例代码.
 *
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */

// ------------------------------------------------------------------------

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

/**
 * SHA1 class
 *
 * 计算公众平台的消息签名接口.
 */
public class SHA1 {

    /**
     * 用SHA1算法验证Token
     *
     * @param token     票据
     * @param timestamp 时间戳
     * @param nonce     随机字符串
     * @return 安全签名
     * @throws AesException
     */
    public static String getSHA1(String token, String timestamp, String nonce) throws AesException {
        try {
            String[] array = new String[]{token, timestamp, nonce};
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(array);
            for (int i = 0; i < 3; i++) {
                sb.append(array[i]);
            }
            String str = sb.toString();
            // SHA1签名生成
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            md.update(str.getBytes());
            byte[] digest = md.digest();

            StringBuffer hexstr = new StringBuffer();
            String shaHex = "";
            for (int i = 0; i < digest.length; i++) {
                shaHex = Integer.toHexString(digest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexstr.append(0);
                }
                hexstr.append(shaHex);
            }
            return hexstr.toString();
        } catch (Exception e) {
            e.printStackTrace();
            throw new AesException(AesException.ComputeSignatureError);
        }
    }
}

AesException

package com.andon.wxPush.utils;

@SuppressWarnings("serial")
public class AesException extends Exception {

	public final static int OK = 0;
	public final static int ValidateSignatureError = -40001;
	public final static int ParseXmlError = -40002;
	public final static int ComputeSignatureError = -40003;
	public final static int IllegalAesKey = -40004;
	public final static int ValidateAppidError = -40005;
	public final static int EncryptAESError = -40006;
	public final static int DecryptAESError = -40007;
	public final static int IllegalBuffer = -40008;
	//public final static int EncodeBase64Error = -40009;
	//public final static int DecodeBase64Error = -40010;
	//public final static int GenReturnXmlError = -40011;

	private int code;

	private static String getMessage(int code) {
		switch (code) {
		case ValidateSignatureError:
			return "签名验证错误";
		case ParseXmlError:
			return "xml解析失败";
		case ComputeSignatureError:
			return "sha加密生成签名失败";
		case IllegalAesKey:
			return "SymmetricKey非法";
		case ValidateAppidError:
			return "appid校验失败";
		case EncryptAESError:
			return "aes加密失败";
		case DecryptAESError:
			return "aes解密失败";
		case IllegalBuffer:
			return "解密后得到的buffer非法";
//		case EncodeBase64Error:
//			return "base64加密错误";
//		case DecodeBase64Error:
//			return "base64解密错误";
//		case GenReturnXmlError:
//			return "xml生成失败";
		default:
			return null; // cannot be
		}
	}

	public int getCode() {
		return code;
	}

	AesException(int code) {
		super(getMessage(code));
		this.code = code;
	}
}

效果

springBoot 实现微信公众号消息自动回复_第2张图片

你可能感兴趣的:(Java,spring,boot,微信,java)