上次已经搭建好了开发环境,接着可以和微信公众号进行对接.

总体思路

  • 服务器的验证 Servlet 用于和微信服务器通信.根据微信公众号开发者文档,微信服务器会发送GET请求给我们所填的URL的服务器.同时带有四个参数:signature,timestamp、nonce,echostr,需要将timestamp、nonce和自己设置的Token排序并进行sha1加密,再与其携带的signature参数比较.如果相等,则该请求来自微信服务器,并返回echostr.
  • 消息的接受和发送

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

开发文档这样写的.所以要从用户发送的消息里提取信息的话,就要先解析xml数据.发送信息个用户也一样,需要将先数据打包成xml再post.

服务器的验证

既然微信服务器发送的是GET请求,新建一个WechatServlet类继承HttpServlet,可以重写Servlet的doGet方法,从请求里得到四个参数,再进行验证.

1
2
3
4
        String signature=request.getParameter("signature");
        String timestamp=request.getParameter("timestamp");
        String nonce=request.getParameter("nonce");
        String echostr=request.getParameter("echostr");

然后就是验证的工作了. 排序:

1
2
3
4
5
6
7
8
        String array[]=new String[]{token,timestamp,nonce};
        //将token、timestamp、nonce三个参数进行字典序排序
        Arrays.sort(array);
        //重组字符串
        StringBuilder builder=new StringBuilder();
        for (int i=0;i<array.length;i++){
            builder.append(array[i]);
        }

加密:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
        MessageDigest md = null;
        String tmpStr = null;
        try {
            md = MessageDigest.getInstance("SHA-1");
            // 将三个参数字符串拼接成一个字符串进行sha1加密
            byte[] digest = md.digest(builder.toString().getBytes());
            tmpStr = byteToStr(digest);
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        builder= null;
        if (tmpStr!=null&tmpStr.equals(signature.toUpperCase())){
            isTrue=true;
        }
        return isTrue;

整体代码,看下图 代码图

填写Token和URL

在微信公众号里填写Token和URL,首先要在web.xml里配置Servlet.要填写的URL就是http://你的域名(或ip)/应用名/Servlet地址.Token 就是自己在验证是设定的值.如果一切都没问题的话, .

消息的接受和被动回复

  • 用到两个开源项目,分别是xStreamdom4j.都是用来处理xml的.
  • 新建一个MsgUtil类用来处理消息(解析xml,把对象打包成xml格式).
  • 接受消息,以文本消息为例,xml的参数如下 xml的数据结构是这样的
1
2
3
4
5
6
7
8
 <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>
  • 可以把这些数据都封装成一个TextMsg对象,如果考虑到其他类型的消息,那么可以有一个BaseMsg类,让TextMsg,ImageMsg等等去继承他.
  • 发送消息的xml结果也一样,少了MsgId这个参数而已.
  • 嗯,每个参数值都有![CDATA[]]标记.待会在发送消息的时候也需要加上标记

接收消息

解析xml

重写doPost方法,把解析出来的数据放入一个Map.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
        throws ServletException,IOException{
        request.setCharacterEncoding("UTF-8");
        response.setCharacterEncoding("UTF-8");
        PrintWriter writer=response.getWriter();
        MsgUtil msgUtil=new MsgUtil();
        String sendMsg=null;
        try {
            //解析xml
            Map<String,String> map=msgUtil.parseXml(request);
            String ToUserName=map.get("ToUserName");
            String FromUserName=map.get("FromUserName");
            Long CreateTime= Long.valueOf(Integer.valueOf(map.get("CreateTime")));
            String Content=map.get("Content");
            String MsgType=map.get("MsgType");
            }catch (Exception e){
            e.printStackTrace();
            }finally {
            if (writer!=null){
                writer.close();
            }
        }

被动发送消息

上面已经从用户发送过来的消息里得到了用户的openid(FromUserName).需要注意的是发送消息时,FromUserName就变成了公众号的Appid,ToUserName才是用户的openid.MsgUtil里添加一个方法:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
    public String initalMessage(String ToUserName,String FromUserName,String Content){
        TextMsg textMsg=new TextMsg();
        textMsg.setContent(Content);
        textMsg.setFromUserName(ToUserName);
        textMsg.setToUserName(FromUserName);
        textMsg.setMsgType(MESSAGE_TEXT);
        textMsg.setCreateTime(new Date().getTime());
        //打包成xml格式
        String xmlmsg=messageToXml(textMsg);
        return xmlmsg;
    }

然后在WechatServlet里调用,

1
2
3
4
5
6
   if (msgUtil.MESSAGE_TEXT.equals(MsgType)){
                sendMsg=msgUtil.initalMessage(ToUserName,FromUserName,"Hello "+Content);
             if (Content.equals("view")){
                    //Todo
             }
   }

打包xml

思考

完成了基本的消息接收和被动的回复.个人感觉处理Json数据比xml数据更加方便,java有封装好的直接处理json的类.微信后台API是不是也应该适配一下,方便开发者?那么下次更新的是Access Token的获取和菜单的创建.==.