微信公众号开发java版-消息回复(普通文字消息和语音消息)

Posted vicf

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了微信公众号开发java版-消息回复(普通文字消息和语音消息)相关的知识,希望对你有一定的参考价值。

关于微信公众号的消息自动回复功能开发,本文将做一个系统介绍,开发语言为java

话不多说,直接上代码

1.控制器代码

package webapp.controller;

import org.springframework.context.annotation.Scope;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import webapp.ext.WechatSignUtil;
import webapp.ext.wechat.AuthProcess;
import webapp.ext.wechat.MessageUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

@Scope("prototype")
@RestControllerpublic class NotifyController 

    @RequestMapping(value = "notify",method =  RequestMethod.GET, RequestMethod.POST )
    @ResponseBody
    public String wechatNotify(HttpServletRequest request, HttpServletResponse response, String signature, String timestamp,
                               String nonce, String echostr) throws IOException 

        boolean isGet = request.getMethod().toLowerCase().equals("get");
        if (isGet) 
            boolean isOk = WechatSignUtil.checkSignature(signature, timestamp, nonce);
            if (isOk)
                return echostr;
            else
                return null;
         else 
            String resp = "";
            request.setCharacterEncoding("UTF-8");
            response.setCharacterEncoding("UTF-8");
            try 
                Map<String,String> map = MessageUtil.xmlToMap(request);
                String xml = "<xml><ToUserName>"+map.get("ToUserName")+"</ToUserName><Encrypt>" +map.get("Encrypt")+
                        "</Encrypt></xml>";
                String result = AuthProcess.decryptMsg(request, xml);
                Map<String, Object> req = MessageUtil.xmlStr2Map(result);

                boolean hasEvent = req.containsKey("Event");

                String respXml = "";
                if (hasEvent) 
                    String event = req.get("Event").toString();
                    if ("subscribe".equals(event)) 
                        //用户订阅公众号行为
                        HashMap<String, Object> mapXml = new HashMap<>();
                        mapXml.put("ToUserName",req.get("FromUserName"));
                        mapXml.put("FromUserName",req.get("ToUserName"));
                        mapXml.put("CreateTime",req.get("CreateTime"));
                        mapXml.put("MsgType","text");
                        mapXml.put("Content","感谢关注");
                        respXml = MessageUtil.map2Xmlstring(mapXml);
                    
                 else 
                    HashMap<String, Object> mapXml = new HashMap<>();
                    mapXml.put("ToUserName",req.get("FromUserName"));
                    mapXml.put("FromUserName",req.get("ToUserName"));
                    mapXml.put("CreateTime",req.get("CreateTime"));
                    mapXml.put("MsgType","text");

                    String msgType = req.get("MsgType").toString();
                    if ("text".equals(msgType))
                        //普通文字消息
                        String content = req.get("Content").toString();
                        respXml = sendMsg(req);
                     else if ("voice".equals(msgType))
                        //语音消息
                        //注意:这里Recognition拿到的直接就是语音消息转化之后的文字内容!!!直接用就好了
                        String msg = req.get("Recognition").toString();
                        respXml = sendMsg(req);
                    
                
                resp = AuthProcess.encryptMsg(request, respXml.trim());
                return resp;
             catch (Exception e) 
                e.printStackTrace();
            
            return resp;
        
    

    private String sendMsg(Map<String, Object> req) 
        HashMap<String, Object> mapXml = new HashMap<>();

        mapXml.put("ToUserName",req.get("FromUserName"));
        mapXml.put("FromUserName",req.get("ToUserName"));
        mapXml.put("CreateTime",req.get("CreateTime"));
        mapXml.put("MsgType","text");
        mapXml.put("Content","消息内容");
        return MessageUtil.map2Xmlstring(mapXml);
    

 

2.其它杂七杂八的一些类(上面控制器中用到的)

package webapp.ext.wechat;

@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;
    

package webapp.ext.wechat;
import javax.servlet.http.HttpServletRequest;

public class AuthProcess 
    /**
     * 将加密后的原文进行解密重新封装
     * @param request
     * @param originalXml 原xml
     * @return    重新解密后的xml
     */
    public static String  decryptMsg(HttpServletRequest request,String originalXml) 
        // 微信加密签名
        //String sVerifyMsgSig = request.getParameter("signature");
        String msgSignature = request.getParameter("msg_signature");
        // 时间戳
        String timestamp = request.getParameter("timestamp");
        // 随机数
        String nonce = request.getParameter("nonce");
        try 
            WXBizMsgCrypt pc = new WXBizMsgCrypt("your token", "your EncodingAESKey", "your appid");
            return pc.decryptMsg(msgSignature, timestamp, nonce, originalXml);
         catch (AesException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
        return null;
    

    /**
     * 对需要回复的原文进行加密重新封装
     * @param request
     * @param replyXml 需要回复的xml
     * @return    重新加密后的xml
     */
    public static String  encryptMsg(HttpServletRequest request, String replyXml) 
        // 时间戳
        String timestamp = request.getParameter("timestamp");
        // 随机数
        String nonce = request.getParameter("nonce");
        try 
            WXBizMsgCrypt pc = new WXBizMsgCrypt("your token","your EncodingAESKey","your appid");
            return pc.encryptMsg(replyXml, timestamp, nonce);
         catch (AesException e) 
            // TODO Auto-generated catch block
            e.printStackTrace();
        
        return null;
    

package webapp.ext.wechat;

import java.util.ArrayList;

class ByteGroup 
    ArrayList<Byte> byteContainer = new ArrayList<Byte>();

    public byte[] toBytes() 
        byte[] bytes = new byte[byteContainer.size()];
        for (int i = 0; i < byteContainer.size(); i++) 
            bytes[i] = byteContainer.get(i);
        
        return bytes;
    

    public ByteGroup addBytes(byte[] bytes) 
        for (byte b : bytes) 
            byteContainer.add(b);
        
        return this;
    

    public int size() 
        return byteContainer.size();
    
package webapp.ext.wechat;

import com.thoughtworks.xstream.XStream;
import org.apache.http.util.TextUtils;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

public class MessageUtil 

    public static final String MESSAGE_TEXT = "text";
    public static final String MESSAGE_IMAGE = "image";
    public static final String MESSAGE_VOICE = "voice";
    public static final String MESSAGE_VIDEO = "video";
    public static final String MESSAGE_LINK = "link";
    public static final String MESSAGE_LOCATION = "location";
    public static final String MESSAGE_EVENT = "event";

    public static final String EVENT_SUB = "subscribe";
    public static final String EVENT_UNSUB = "unsubscribe";
    public static final String EVENT_CLICK = "CLICK";
    public static final String EVENT_VIEW = "VIEW";

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

        SAXReader reader = new SAXReader();

        InputStream ins = request.getInputStream();
        Document doc = reader.read(ins);

        Element root = doc.getRootElement();
        List<Element> list = root.elements();
        for (Element e : list) 
            map.put(e.getName(), e.getText());
        
        ins.close();
        return map;
    

    public static String textMessageToXml(TextMessage textMessage)
        XStream xstream = new XStream();
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);

    

    public static Map<String,Object> xmlStr2Map(String xmlStr)
        Map<String,Object> map = new HashMap<String,Object>();
        Document doc;
        try 
            doc = DocumentHelper.parseText(xmlStr);
            Element root = doc.getRootElement();
            List children = root.elements();
            if(children != null && children.size() > 0) 
                for(int i = 0; i < children.size(); i++) 
                    Element child = (Element)children.get(i);
                    map.put(child.getName(), child.getTextTrim());
                
            
         catch (DocumentException e) 
            e.printStackTrace();
        
        return map;
    


    public static String map2Xmlstring(Map<String,Object> map)
        StringBuffer sb = new StringBuffer("");
        sb.append("<xml>");

        Set<String> set = map.keySet();
        for(Iterator<String> it = set.iterator(); it.hasNext();)
            String key = it.next();
            Object value = map.get(key);
            sb.append("<").append(key).append(">");
            sb.append(value);
            sb.append("</").append(key).append(">");
        
        sb.append("</xml>");
        return sb.toString();
    

    public static String map2XmlstringWithHead(Map<String,Object> map,String head)
        StringBuffer sb = new StringBuffer("");

        if (!TextUtils.isEmpty(head)) 
            sb.append("<"+head+">");
        
        Set<String> set = map.keySet();
        for(Iterator<String> it = set.iterator(); it.hasNext();)
            String key = it.next();
            Object value = map.get(key);
            sb.append("<").append(key).append(">");
            sb.append(value);
            sb.append("</").append(key).append(">");
        
        if (!TextUtils.isEmpty(head)) 
            sb.append("</"+head+">");
        
        return sb.toString();
    

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

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

package webapp.ext.wechat;

import java.nio.charset.Charset;
import java.util.Arrays;

/**
 * 提供基于PKCS7算法的加解密接口.
 */
class PKCS7Encoder 
    static Charset CHARSET = Charset.forName("utf-8");
    static int BLOCK_SIZE = 32;

    /**
     * 获得对明文进行补位填充的字节.
     * 
     * @param count 需要进行填充补位操作的明文字节个数
     * @return 补齐用的字节数组
     */
    static byte[] encode(int count) 
        // 计算需要填充的位数
        int amountToPad = BLOCK_SIZE - (count % BLOCK_SIZE);
        if (amountToPad == 0) 
            amountToPad = BLOCK_SIZE;
        
        // 获得补位所用的字符
        char padChr = chr(amountToPad);
        String tmp = new String();
        for (int index = 0; index < amountToPad; index++) 
            tmp += padChr;
        
        return tmp.getBytes(CHARSET);
    

    /**
     * 删除解密后明文的补位字符
     * 
     * @param decrypted 解密后的明文
     * @return 删除补位字符后的明文
     */
    static byte[] decode(byte[] decrypted) 
        int pad = (int) decrypted[decrypted.length - 1];
        if (pad < 1 || pad > 32) 
            pad = 0;
        
        return Arrays.copyOfRange(decrypted, 0, decrypted.length - pad);
    

    /**
     * 将数字转化成ASCII码对应的字符,用于对明文进行补码
     * 
     * @param a 需要转化的数字
     * @return 转化得到的字符
     */
    static char chr(int a) 
        byte target = (byte) (a & 0xFF);
        return (char) target;
    

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

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

package webapp.ext.wechat;

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

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

    /**
     * 用SHA1算法生成安全签名
     * @param token 票据
     * @param timestamp 时间戳
     * @param nonce 随机字符串
     * @param encrypt 密文
     * @return 安全签名
     * @throws com.qq.weixin.mp.aes.AesException
     */
    public static String getSHA1(String token, String timestamp, String nonce, String encrypt) throws AesException
              
        try 
            String[] array = new String[]  token, timestamp, nonce, encrypt ;
            StringBuffer sb = new StringBuffer();
            // 字符串排序
            Arrays.sort(array);
            for (int i = 0; i < 4; 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);
        
    
package webapp.ext.wechat;

public class TextMessage 

    public String ToUserName;
    public String FromUserName;
    public int CreateTime;
    public String MsgType;
    public String Content;

    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 int getCreateTime() 
        return CreateTime;
    

    public void setCreateTime(int 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;
    
/**
 * 对公众平台发送给公众账号的消息加解密示例代码.
 * 
 * @copyright Copyright (c) 1998-2014 Tencent Inc.
 */

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

/**
 * 针对org.apache.commons.codec.binary.Base64,
 * 需要导入架包commons-codec-1.9(或commons-codec-1.8等其他版本)
 * 官方下载地址:http://commons.apache.org/proper/commons-codec/download_codec.cgi
 */
package webapp.ext.wechat;

import org.apache.commons.codec.binary.Base64;

import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.Charset;
import java.util.Arrays;
import java.util.Random;

/**
 * 提供接收和推送给公众平台消息的加解密接口(UTF8编码的字符串).
 * <ol>
 *     <li>第三方回复加密消息给公众平台</li>
 *     <li>第三方收到公众平台发送的消息,验证消息的安全性,并对消息进行解密。</li>
 * </ol>
 * 说明:异常java.security.InvalidKeyException:illegal Key Size的解决方案
 * <ol>
 *     <li>在官方网站下载JCE无限制权限策略文件(JDK7的下载地址:
 *      http://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html</li>
 *     <li>下载后解压,可以看到local_policy.jar和US_export_policy.jar以及readme.txt</li>
 *     <li>如果安装了JRE,将两个jar文件放到%JRE_HOME%\\lib\\security目录下覆盖原来的文件</li>
 *     <li>如果安装了JDK,将两个jar文件放到%JDK_HOME%\\jre\\lib\\security目录下覆盖原来文件</li>
 * </ol>
 */
public class WXBizMsgCrypt 
    static Charset CHARSET = Charset.forName("utf-8");
    Base64 base64 = new Base64();
    byte[] aesKey;
    String token;
    String appId;

    /**
     * 构造函数
     * @param token 公众平台上,开发者设置的token
     * @param encodingAesKey 公众平台上,开发者设置的EncodingAESKey
     * @param appId 公众平台appid
     * 
     * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
     */
    public WXBizMsgCrypt(String token, String encodingAesKey, String appId) throws AesException 
        if (encodingAesKey.length() != 43) 
            throw new AesException(AesException.IllegalAesKey);
        

        this.token = token;
        this.appId = appId;
        aesKey = Base64.decodeBase64(encodingAesKey + "=");
    

    // 生成4个字节的网络字节序
    byte[] getNetworkBytesOrder(int sourceNumber) 
        byte[] orderBytes = new byte[4];
        orderBytes[3] = (byte) (sourceNumber & 0xFF);
        orderBytes[2] = (byte) (sourceNumber >> 8 & 0xFF);
        orderBytes[1] = (byte) (sourceNumber >> 16 & 0xFF);
        orderBytes[0] = (byte) (sourceNumber >> 24 & 0xFF);
        return orderBytes;
    

    // 还原4个字节的网络字节序
    int recoverNetworkBytesOrder(byte[] orderBytes) 
        int sourceNumber = 0;
        for (int i = 0; i < 4; i++) 
            sourceNumber <<= 8;
            sourceNumber |= orderBytes[i] & 0xff;
        
        return sourceNumber;
    

    // 随机生成16位字符串
    String getRandomStr() 
        String base = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
        Random random = new Random();
        StringBuffer sb = new StringBuffer();
        for (int i = 0; i < 16; i++) 
            int number = random.nextInt(base.length());
            sb.append(base.charAt(number));
        
        return sb.toString();
    

    /**
     * 对明文进行加密.
     * 
     * @param text 需要加密的明文
     * @return 加密后base64编码的字符串
     * @throws AesException aes加密失败
     */
    String encrypt(String randomStr, String text) throws AesException 
        ByteGroup byteCollector = new ByteGroup();
        byte[] randomStrBytes = randomStr.getBytes(CHARSET);
        byte[] textBytes = text.getBytes(CHARSET);
        byte[] networkBytesOrder = getNetworkBytesOrder(textBytes.length);
        byte[] appidBytes = appId.getBytes(CHARSET);

        // randomStr + networkBytesOrder + text + appid
        byteCollector.addBytes(randomStrBytes);
        byteCollector.addBytes(networkBytesOrder);
        byteCollector.addBytes(textBytes);
        byteCollector.addBytes(appidBytes);

        // ... + pad: 使用自定义的填充方式对明文进行补位填充
        byte[] padBytes = PKCS7Encoder.encode(byteCollector.size());
        byteCollector.addBytes(padBytes);

        // 获得最终的字节流, 未加密
        byte[] unencrypted = byteCollector.toBytes();

        try 
            // 设置加密模式为AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec keySpec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(aesKey, 0, 16);
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, iv);

            // 加密
            byte[] encrypted = cipher.doFinal(unencrypted);

            // 使用BASE64对加密后的字符串进行编码
            String base64Encrypted = base64.encodeToString(encrypted);

            return base64Encrypted;
         catch (Exception e) 
            e.printStackTrace();
            throw new AesException(AesException.EncryptAESError);
        
    

    /**
     * 对密文进行解密.
     * 
     * @param text 需要解密的密文
     * @return 解密得到的明文
     * @throws AesException aes解密失败
     */
    String decrypt(String text) throws AesException 
        byte[] original;
        try 
            // 设置解密模式为AES的CBC模式
            Cipher cipher = Cipher.getInstance("AES/CBC/NoPadding");
            SecretKeySpec key_spec = new SecretKeySpec(aesKey, "AES");
            IvParameterSpec iv = new IvParameterSpec(Arrays.copyOfRange(aesKey, 0, 16));
            cipher.init(Cipher.DECRYPT_MODE, key_spec, iv);

            // 使用BASE64对密文进行解码
            byte[] encrypted = Base64.decodeBase64(text);

            // 解密
            original = cipher.doFinal(encrypted);
         catch (Exception e) 
            e.printStackTrace();
            throw new AesException(AesException.DecryptAESError);
        

        String xmlContent, from_appid;
        try 
            // 去除补位字符
            byte[] bytes = PKCS7Encoder.decode(original);

            // 分离16位随机字符串,网络字节序和AppId
            byte[] networkOrder = Arrays.copyOfRange(bytes, 16, 20);

            int xmlLength = recoverNetworkBytesOrder(networkOrder);

            xmlContent = new String(Arrays.copyOfRange(bytes, 20, 20 + xmlLength), CHARSET);
            from_appid = new String(Arrays.copyOfRange(bytes, 20 + xmlLength, bytes.length),
                    CHARSET);
         catch (Exception e) 
            e.printStackTrace();
            throw new AesException(AesException.IllegalBuffer);
        

        // appid不相同的情况
        if (!from_appid.equals(appId)) 
            throw new AesException(AesException.ValidateAppidError);
        
        return xmlContent;

    

    /**
     * 将公众平台回复用户的消息加密打包.
     * <ol>
     *     <li>对要发送的消息进行AES-CBC加密</li>
     *     <li>生成安全签名</li>
     *     <li>将消息密文和安全签名打包成xml格式</li>
     * </ol>
     * 
     * @param replyMsg 公众平台待回复用户的消息,xml格式的字符串
     * @param timeStamp 时间戳,可以自己生成,也可以用URL参数的timestamp
     * @param nonce 随机串,可以自己生成,也可以用URL参数的nonce
     * 
     * @return 加密后的可以直接回复用户的密文,包括msg_signature, timestamp, nonce, encrypt的xml格式的字符串
     * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
     */
    public String encryptMsg(String replyMsg, String timeStamp, String nonce) throws AesException 
        // 加密
        String encrypt = encrypt(getRandomStr(), replyMsg);

        // 生成安全签名
        if (timeStamp == "") 
            timeStamp = Long.toString(System.currentTimeMillis());
        

        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt);

        // System.out.println("发送给平台的签名是: " + signature[1].toString());
        // 生成发送的xml
        String result = XMLParse.generate(encrypt, signature, timeStamp, nonce);
        return result;
    

    /**
     * 检验消息的真实性,并且获取解密后的明文.
     * <ol>
     *     <li>利用收到的密文生成安全签名,进行签名验证</li>
     *     <li>若验证通过,则提取xml中的加密消息</li>
     *     <li>对消息进行解密</li>
     * </ol>
     * 
     * @param msgSignature 签名串,对应URL参数的msg_signature
     * @param timeStamp 时间戳,对应URL参数的timestamp
     * @param nonce 随机串,对应URL参数的nonce
     * @param postData 密文,对应POST请求的数据
     * 
     * @return 解密后的原文
     * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
     */
    public String decryptMsg(String msgSignature, String timeStamp, String nonce, String postData)
            throws AesException 

        // 密钥,公众账号的app secret
        // 提取密文
        Object[] encrypt = XMLParse.extract(postData);

        // 验证安全签名
        String signature = SHA1.getSHA1(token, timeStamp, nonce, encrypt[1].toString());

        // 和URL中的签名比较是否相等
        // System.out.println("第三方收到URL中的签名:" + msg_sign);
        // System.out.println("第三方校验签名:" + signature);
        if (!signature.equals(msgSignature)) 
            throw new AesException(AesException.ValidateSignatureError);
        

        // 解密
        String result = decrypt(encrypt[1].toString());
        return result;
    

    /**
     * 验证URL
     * @param msgSignature 签名串,对应URL参数的msg_signature
     * @param timeStamp 时间戳,对应URL参数的timestamp
     * @param nonce 随机串,对应URL参数的nonce
     * @param echoStr 随机串,对应URL参数的echostr
     * 
     * @return 解密之后的echostr
     * @throws AesException 执行失败,请查看该异常的错误码和具体的错误信息
     */
    public String verifyUrl(String msgSignature, String timeStamp, String nonce, String echoStr)
            throws AesException 
        String signature = SHA1.getSHA1(token, timeStamp, nonce, echoStr);

        if (!signature.equals(msgSignature)) 
            throw new AesException(AesException.ValidateSignatureError);
        

        String result = decrypt(echoStr);
        return result;
    

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

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

package webapp.ext.wechat;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import java.io.StringReader;

/**
 * XMLParse class
 *
 * 提供提取消息格式中的密文及生成回复消息格式的接口.
 */
class XMLParse 

    /**
     * 提取出xml数据包中的加密消息
     * @param xmltext 待提取的xml字符串
     * @return 提取出的加密消息字符串
     * @throws com.qq.weixin.mp.aes.AesException
     */
    public static Object[] extract(String xmltext) throws AesException 
        Object[] result = new Object[3];
        try 
            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            dbf.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
            dbf.setFeature("http://xml.org/sax/features/external-general-entities", false);
            dbf.setFeature("http://xml.org/sax/features/external-parameter-entities", false);
            dbf.setFeature("http://apache.org/xml/features/nonvalidating/load-external-dtd", false);
            dbf.setXIncludeAware(false);
            dbf.setExpandEntityReferences(false);
            DocumentBuilder db = dbf.newDocumentBuilder();
            StringReader sr = new StringReader(xmltext);
            InputSource is = new InputSource(sr);
            Document document = db.parse(is);

            Element root = document.getDocumentElement();
            NodeList nodelist1 = root.getElementsByTagName("Encrypt");
            NodeList nodelist2 = root.getElementsByTagName("ToUserName");
            result[0] = 0;
            result[1] = nodelist1.item(0).getTextContent();
            result[2] = nodelist2.item(0).getTextContent();
            return result;
         catch (Exception e) 
            e.printStackTrace();
            throw new AesException(AesException.ParseXmlError);
        
    

    /**
     * 生成xml消息
     * @param encrypt 加密后的消息密文
     * @param signature 安全签名
     * @param timestamp 时间戳
     * @param nonce 随机字符串
     * @return 生成的xml字符串
     */
    public static String generate(String encrypt, String signature, String timestamp, String nonce) 

        String format = "<xml>\\n" + "<Encrypt><![CDATA[%1$s]]></Encrypt>\\n"
                + "<MsgSignature><![CDATA[%2$s]]></MsgSignature>\\n"
                + "<TimeStamp>%3$s</TimeStamp>\\n" + "<Nonce><![CDATA[%4$s]]></Nonce>\\n" + "</xml>";
        return String.format(format, encrypt, signature, timestamp, nonce);

    
package webapp.ext;


import webapp.config.Config;

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

/**
 * @Description:微信签名校验
 */
public class WechatSignUtil 


    /**
     * 校验签名
     * @param signature 微信加密签名.
     * @param timestamp 时间戳.
     * @param nonce 随机数.
     * @return
     */
    public static boolean checkSignature(String signature, String timestamp, String nonce) 
        // 对token、timestamp、和nonce按字典排序.
        String[] paramArr = new String[] "你的公众号token", timestamp, nonce;
        Arrays.sort(paramArr);

        // 将排序后的结果拼接成一个字符串.
        String content  = paramArr[0].concat(paramArr[1]).concat(paramArr[2]);

        String ciphertext = null;
        try 
            MessageDigest md = MessageDigest.getInstance("SHA-1");
            // 对拼接后的字符串进行sha1加密.
            byte[] digest = md.digest(content.toString().getBytes());
            ciphertext = byteToStr(digest);
         catch (NoSuchAlgorithmException e) 
            e.printStackTrace();
        

        // 将sha1加密后的字符串与signature进行对比.
        return ciphertext != null ? ciphertext.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;
    

//    //获取token
//    public static String getToken() throws Exception
//        String token = DbGhostApi.tokenIsValid();
//        if (token != null) 
//            return token;
//         else 
//            Date now = new Date();
//            String rsp = HttpUtil.getString("https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=" + Config.appid + "&secret=" + Config.secret);
//            JSONObject obj = JSONObject.parseObject(rsp);
//            String access_token = obj.getString("access_token");
//            int expires_in = obj.getIntValue("expires_in");
//            DbGhostApi.updateToken(access_token, DateUtil.dateAddSecond(now,expires_in));
//            return access_token;
//        
//    

 

注意:语音消息识别需要在公众号后台开启相应功能

技术图片

 

以上是关于微信公众号开发java版-消息回复(普通文字消息和语音消息)的主要内容,如果未能解决你的问题,请参考以下文章

JAVA微信公众号开发回复消息能回复多条吗?具体怎么代码实现?

Java微信公众平台开发--回复消息的分类及实体的创建

微信公众号教程(10)公众账号自定义回复功能

微信公众号开发--消息的接收与被动回复消息

给微信公众号发消息,他能收到吗?

微信开发公众号自动回复文字和图文链接