微信公众号开发--接收与回复消息(Java)

第一步:配置微信公众号

进入微信公众平台地址

微信公众号开发--接收与回复消息(Java)_第1张图片

进入基本配置

微信公众号开发--接收与回复消息(Java)_第2张图片

这里需要填写的有服务器的URL、Token两个地方

URL

是一个公网地址,只能接收80端口的(http默认端口)

http://公网地址/项目名称/请求路径

Token

在校验的时候需要用到,随便输入一个字符串就可以了

开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数
参数 描述
signature 微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串

所以需要编写相应的Controller(采用Spring MVC)

    /** * 验证微信服务器 * * @param response * @param signature * @param timestamp * @param nonce * @param echostr */
    @RequestMapping(value = "/wechat", method = RequestMethod.GET)
    public void wechatService(PrintWriter out, HttpServletResponse response,
            @RequestParam(value = "signature", required = false) String signature, @RequestParam String timestamp,
            @RequestParam String nonce, @RequestParam String echostr) {
        if (CheckUtil.checkSignature(signature, timestamp, nonce)) {
            out.print(echostr);
        }
    }

CheckUtil.java

package com.wechat.utils;

import java.util.Arrays;

/** * * 校验工具类 * 开发者通过检验signature对请求进行校验(下面有校验方式)。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效, * 成为开发者成功,否则接入失败。 * * @author GWCheng * */
public class CheckUtil {
    //配置微信公众号时填写的Token
    private static final String token = "chenggaowei";

    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 tmp = DecriptUtil.SHA1(content.toString());
        return tmp.equals(signature);
    }
}

DecriptUtil.java

import java.io.UnsupportedEncodingException;
import java.security.InvalidKeyException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;

/** * 各种加密解密 * */
public class DecriptUtil {

    public static String SHA1(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA-1");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String SHA(String decript) {
        try {
            MessageDigest digest = java.security.MessageDigest
                    .getInstance("SHA");
            digest.update(decript.getBytes());
            byte messageDigest[] = digest.digest();
            // Create Hex String
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < messageDigest.length; i++) {
                String shaHex = Integer.toHexString(messageDigest[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();

        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    public static String MD5(String input) {
        try {
            // 获得MD5摘要算法的 MessageDigest 对象
            MessageDigest mdInst = MessageDigest.getInstance("MD5");
            // 使用指定的字节更新摘要
            mdInst.update(input.getBytes());
            // 获得密文
            byte[] md = mdInst.digest();
            // 把密文转换成十六进制的字符串形式
            StringBuffer hexString = new StringBuffer();
            // 字节数组转换为 十六进制 数
            for (int i = 0; i < md.length; i++) {
                String shaHex = Integer.toHexString(md[i] & 0xFF);
                if (shaHex.length() < 2) {
                    hexString.append(0);
                }
                hexString.append(shaHex);
            }
            return hexString.toString();
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return "";
    }

    /** * 加密 * * @param content * 需要加密的内容 * @param password * 加密密码 * @return */
    public static byte[] encryptAES(String content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            byte[] byteContent = content.getBytes("utf-8");
            cipher.init(Cipher.ENCRYPT_MODE, key);// 初始化
            byte[] result = cipher.doFinal(byteContent);
            return result; // 加密
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /** * 解密 * * @param content * 待解密内容 * @param password * 解密密钥 * @return */
    public static byte[] decryptAES(byte[] content, String password) {
        try {
            KeyGenerator kgen = KeyGenerator.getInstance("AES");
            kgen.init(128, new SecureRandom(password.getBytes()));
            SecretKey secretKey = kgen.generateKey();
            byte[] enCodeFormat = secretKey.getEncoded();
            SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
            Cipher cipher = Cipher.getInstance("AES");// 创建密码器
            cipher.init(Cipher.DECRYPT_MODE, key);// 初始化
            byte[] result = cipher.doFinal(content);
            return result; // 加密
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        } catch (NoSuchPaddingException e) {
            e.printStackTrace();
        } catch (InvalidKeyException e) {
            e.printStackTrace();
        } catch (IllegalBlockSizeException e) {
            e.printStackTrace();
        } catch (BadPaddingException e) {
            e.printStackTrace();
        }
        return null;
    }

    /** * BASE64解密 * * @param key * @return * @throws Exception */
    public static String decryptBASE64(String key) {

        return "";
    }

    /** * BASE64加密 * * @param key * @return * @throws Exception */
    public static String encryptBASE64(String key) {

        return "";
    }
}

验证写好了就可以点击提交了

配置成功之后会有如下的提示信息

这里写图片描述

下面是微信发过来的四个参数,以及给微信回复的echostr

这里写图片描述

至此开发环境已经配置好了!

第二步:接收消息

环境配置好之后就可以开发了

微信会将发往公众账号的消息发到我们配置的URL中,以POST的方式

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

以下是debug的信息

微信已经将发往公众账号的消息发到了我们配置的服务器地址

xml消息不太利于编写程序,所以需要对xml消息进行相应的转换,这里将其转会为map

将xml转换为map

    /** * 将xml转化为Map集合 * * @param request * @return */
    public static Map<String, String> xmlToMap(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        SAXReader reader = new SAXReader();
        InputStream ins = null;
        try {
            ins = request.getInputStream();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        Document doc = null;
        try {
            doc = reader.read(ins);
        } catch (DocumentException e1) {
            e1.printStackTrace();
        }
        Element root = doc.getRootElement();
        @SuppressWarnings("unchecked")
        List<Element> list = root.elements();
        for (Element e : list) {
            map.put(e.getName(), e.getText());
        }
        try {
            ins.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return map;
    }

用到的jar包

dom4j.jar

这个方法写好之后就可以调用了:

Controller如下,微信发过来的消息用POST接收

    /** * 接收来自微信发来的消息 * * @param out * @param request * @param response */
    @ResponseBody
    @RequestMapping(value = "/wechat", method = RequestMethod.POST)
    public void wechatServicePost(PrintWriter out, HttpServletRequest request, HttpServletResponse response) {
        String responseMessage = wechatService.processRequest(request);
        out.print(responseMessage);
        out.flush();
    }

Controller调用Service的方法(给用户返回消息,也是一个xml格式的字符串)

Service关键代码如下

@Service
public class WechatService {
    private static Logger log = Logger.getLogger(WechatService.class);

    public String processRequest(HttpServletRequest request) {
        Map<String, String> map = WechatMessageUtil.xmlToMap(request);
        log.info(map);
        // 发送方帐号(一个OpenID)
        String fromUserName = map.get("FromUserName");
        // 开发者微信号
        String toUserName = map.get("ToUserName");
        // 消息类型
        String msgType = map.get("MsgType");
        // 默认回复一个"success"
        String responseMessage = "success";
        // 对消息进行处理
        if (WechatMessageUtil.MESSAGE_TEXT.equals(msgType)) {// 文本消息
            TextMessage textMessage = new TextMessage();
            textMessage.setMsgType(WechatMessageUtil.MESSAGE_TEXT);
            textMessage.setToUserName(fromUserName);
            textMessage.setFromUserName(toUserName);
            textMessage.setCreateTime(System.currentTimeMillis());
            textMessage.setContent("我已经受到你发来的消息了");
            responseMessage = WechatMessageUtil.textMessageToXml(textMessage);
        }
        log.info(responseMessage);
        return responseMessage;

    }

}

Servie调用WechatMessageUtil将reques中的xml转换为map

WechatMessageUtil.java如下


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 com.wechat.entity.vo.wechat.message.TextMessage;

public class WechatMessageUtil {
    // 各种消息类型,除了扫带二维码事件
    /** * 文本消息 */
    public static final String MESSAGE_TEXT = "text";
    /** * 图片消息 */
    public static final String MESSAtGE_IMAGE = "image";
    /** * 图文消息 */
    public static final String MESSAGE_NEWS = "news";
    /** * 语音消息 */
    public static final String MESSAGE_VOICE = "voice";
    /** * 视频消息 */
    public static final String MESSAGE_VIDEO = "video";
    /** * 小视频消息 */
    public static final String MESSAGE_SHORTVIDEO = "shortvideo";
    /** * 地理位置消息 */
    public static final String MESSAGE_LOCATION = "location";
    /** * 链接消息 */
    public static final String MESSAGE_LINK = "link";
    /** * 事件推送消息 */
    public static final String MESSAGE_EVENT = "event";
    /** * 事件推送消息中,事件类型,subscribe(订阅) */
    public static final String MESSAGE_EVENT_SUBSCRIBE = "subscribe";
    /** * 事件推送消息中,事件类型,unsubscribe(取消订阅) */
    public static final String MESSAGE_EVENT_UNSUBSCRIBE = "unsubscribe";
    /** * 事件推送消息中,上报地理位置事件 */
    public static final String MESSAGE_EVENT_LOCATION_UP = "LOCATION";
    /** * 事件推送消息中,自定义菜单事件,点击菜单拉取消息时的事件推送 */
    public static final String MESSAGE_EVENT_CLICK = "CLICK";
    /** * 事件推送消息中,自定义菜单事件,点击菜单跳转链接时的事件推送 */
    public static final String MESSAGE_EVENT_VIEW = "VIEW";

    /** * 将xml转化为Map集合 * * @param request * @return */
    public static Map<String, String> xmlToMap(HttpServletRequest request) {
        Map<String, String> map = new HashMap<String, String>();
        SAXReader reader = new SAXReader();
        InputStream ins = null;
        try {
            ins = request.getInputStream();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        Document doc = null;
        try {
            doc = reader.read(ins);
        } catch (DocumentException e1) {
            e1.printStackTrace();
        }
        Element root = doc.getRootElement();
        @SuppressWarnings("unchecked")
        List<Element> list = root.elements();
        for (Element e : list) {
            map.put(e.getName(), e.getText());
        }
        try {
            ins.close();
        } catch (IOException e1) {
            e1.printStackTrace();
        }
        return map;
    }

    /** * 文本消息转化为xml * * @param textMessage * @return */
    public static String textMessageToXml(TextMessage textMessage) {
        XStream xstream = new XStream();
        xstream.alias("xml", textMessage.getClass());
        return xstream.toXML(textMessage);

    }

}

用到的jar包有

log4j
xstream
xmlpull

用户发来的消息

{MsgId=6241406265466888851, FromUserName=od_Ldw8_rwcGiFsaCeF74Vsc-qz4, CreateTime=1453190638, Content=你好, ToUserName=gh_8f6a0cd44617, MsgType=text}

给用户回复的消息

<xml>
      <ToUserName>od_Ldw8_rwcGiFsaCeF74Vsc-qz4</ToUserName>
      <FromUserName>gh_8f6a0cd44617</FromUserName>
      <CreateTime>1453190640848</CreateTime>
      <MsgType>text</MsgType>
      <Content>我已经受到你发来的消息了</Content>
</xml>

如图所示

微信公众号开发--接收与回复消息(Java)_第3张图片

完整项目下载地址

https://github.com/peer44/testwechat

这个地址中的内容在更新,如果觉得有帮助,希望能关注我的动态

参考文献

微信开发者文档

你可能感兴趣的:(微信公众号开发)