微信公众号开发笔记之收发消息

        最近项目需要开发一个微信公众号作为其中一个模块的功能入口,花了两天时间简单了解了一下微信公众号的开发流程,这里简单总结一下。

1. 效果图—发送消息简单的回复“您好,感谢关注本公众号!”

这里写图片描述

2. 学习用的是测试号,测试号申请流程:

(1)打开微信测试号地址:点此跳转登录后如下图

这里写图片描述

(2) 上图中需要填写url地址和token

        开发者提交信息(包括URL、Token)后,微信服务器将发送Http Get请求到填写的URL上,GET请求携带四个参数:signature、timestamp、nonce和echostr。公众号服务程序应该按如下要求进行接入验证:
  1. 将token、timestamp、nonce三个参数进行字典序排序
  2. 将三个参数字符串拼接成一个字符串进行sha1加密
  3. 将加密后获得的字符串与signature对比,如果一致,则原样返回echostr参数内容

(3)使用Ngrok内网穿透工具将localhos:8080的本地地址映射到外网

        详细教程请参考Sunny的博客,下载并开通好隧道服务以后,在ngrok解压后目录执行一下命令:

./sunny clientid 隧道id

此时就可以直接在浏览器输入地址,如http://example.ngrok.cc,来访问本地的服务器了

(4)进入正题,服务器介入验证(这里使用原生的servlet来实现)

在doget方法里取出四个参数

protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String timestamp = request.getParameter("timestamp");
        String nonce = request.getParameter("nonce");
        String echostr = request.getParameter("echostr");
        String signature = request.getParameter("signature");
        PrintWriter out = response.getWriter();
        if(CheckUtil.checkSignature(signature, timestamp, nonce)){//CheckUtil是验证工具类
            out.print(echostr);
        }
    }

编写CheckUtil类进行sha1加密并与signature进行比较

public class CheckUtil {
    private static final String token = "example";

    public static boolean checkSignature(String signature, String timestamp, String nonce){
        ArrayList<String> list = new ArrayList<>();
        list.add(token);
        list.add(timestamp);
        list.add(nonce);
        Collections.sort(list);
        StringBuffer content = new StringBuffer();
        for(String item : list){
            content.append(item);//连成字符串进行加密
        }
        String result = getSha1(content.toString());
        return signature.equals(result)?true:false;
    }

    /**
     * SHA1加密方法
     */
    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;
        }
    }
}

如果checkSignature比较后返回true,说明该消息是来自微信服务器,则将echostr原样返回

(5)顺利的话,在测试号申请页面,点击提交按钮以后便会提示成功,并且出现如下界面

这里写图片描述
扫一下二维码就可以关注自己的公众号啦,虽然只是一个测试号,但是还是很激动滴。。。不过这时候你不论向公众号发送多少消息,返回的都是“该公众号暂时无法提供服务,请稍后再试”。。。。。

(6)接下来实现收发文本消息的功能。首先查看一下微信公众平台的开发者规范消息管理这部分:

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

文本消息
<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 开发者微信号
ToUserName 开发者微信号
FromUserName 发送方帐号(一个OpenID)
CreateTime 消息创建时间 (整型)
MsgType text
Content 文本消息内容
MsgId 消息id,64位整型

在doPost方法中接受消息:

protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter out = response.getWriter();
        try {
            //将xml请求信息转换为map形式保存
            Map<String, String> map = MsgUtils.xmlToMap(request);
            TextMessage respMsg = new TextMessage();
            //返回"您好,感谢您关注本公众号!"消息
            respMsg.setToUserName(map.get("FromUserName"));
            respMsg.setFromUserName(map.get("ToUserName"));
            respMsg.setCreateTime(new Date().toString());
            respMsg.setContent("您好,感谢您关注本公众号!");
            respMsg.setMsgType(MsgUtils.MESSAGE_TEXT);
            //将发送的信息转换为xml字符串
            String respXml = MsgUtils.textToXml(respMsg);
            out.println(respXml);
        } catch (DocumentException e) {
            e.printStackTrace();
        }finally{
            out.close();
        }

    }

上例中TextMessage是经过封装的一个消息的javabean类:

    private String ToUserName;
    private String FromUserName;
    private String CreateTime;
    private String MsgType;
    private String Content;

MsgUtils是用于将消息文本和xml格式字符串相互转换的工具类:

public class MsgUtils {
    public static final String MESSAGE_TEXT = "text";

    public static Map<String, String> xmlToMap(HttpServletRequest req) throws IOException, DocumentException{
        Map<String, String> resultMap = new HashMap<>();
        //获取SAXReader
        SAXReader reader = new SAXReader();
        //获取request输入流
        InputStream is = req.getInputStream();
        //转换为Document对象
        Document document = reader.read(is);
        //获取根节点
        Element root = document.getRootElement();
        //获取元素集合
        List<Element> elements = root.elements();
        for(Element e : elements){
            System.out.println("e.getName()>>>"+e.getName()+"  e.getText()>>>"+e.getText());
            resultMap.put(e.getName(), e.getText());
        }
        is.close();
        return resultMap;
    }

    public static String textToXml(TextMessage textMessage){
        Document document = DocumentHelper.createDocument();
        //创建根节点<xml><xml>
        Element rootElement = document.addElement("xml");
        //将节点添加到根节点中
        Element ToUserName = rootElement.addElement("ToUserName");
        ToUserName.setText(textMessage.getToUserName());

        Element FromUserName = rootElement.addElement("FromUserName");
        FromUserName.setText(textMessage.getFromUserName());

        Element CreateTime = rootElement.addElement("CreateTime");
        CreateTime.setText(textMessage.getCreateTime());

        Element MsgType = rootElement.addElement("MsgType");
        MsgType.setText(textMessage.getMsgType());

        Element Content = rootElement.addElement("Content");
        Content.setText(textMessage.getContent());

        //将xml的document对象转化为字符串,打印查看一下
        System.out.println(document.asXML());
        return document.asXML();
    } 

}

xml转换使用到了dom4j的jar包,经过上面的6步就实现了接受关注的用户发来的消息并且返回一条消息。再此之上,可以详细的封装一个消息内容详情类,比如收到“1”,返回“您发送了1”,等等文本信息。下一篇再写其他类型的消息,篇幅比较长,本人也是初学者,不对的地方还请指出来,互相学习

猜你喜欢

转载自blog.csdn.net/time_travel/article/details/70832900