微信开发回复信息

2016-05-12 17:42:56 qq_29407009 阅读数 1312

在前面我实现了配置微信接口的配置,将自己的订阅号的控制交给我自己控制,用户回复消息,微信的后台会通过发送一个post请求到自己的服务器,是发送一个XML
消息的接收:
文本消息(在微信的开发者文档中能看到)

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

自己写一个方法,解析xml,将所有的内容解析完成之后,存放在一个Map中(首先设置一下编码集),
用到的jar包:dom4j-1.6.1.jar

```
//servlet中调用
request.setCharacterEncoding("utf-8");//设置编码集
response.setCharacterEncoding("utf-8");//设置编码集
Map<String,String> map=MessageUtils.xmlToMap(request);
//解析xml的方法
public static Map<String,String> xmlToMap(HttpServletRequest request){
        Map<String,String> map=new HashMap<String,String>();
        SAXReader reader=new  SAXReader();
        InputStream inputStream=null;
        try {
            inputStream = request.getInputStream();
            Document document=reader.read(inputStream);
            Element root=document.getRootElement();
            @SuppressWarnings("unchecked")
            List<Element> list=root.elements();
            for (Element e : list) {
                map.put(e.getName(), e.getText());
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (DocumentException e) {
            e.printStackTrace();
        }finally{
            if (inputStream!=null) {
                try {
                    inputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return map;
    }
```
将解析微信后台发送给我的xml后得到map,再在map中取值

```
String toUserName=map.get("ToUserName");//自己公众号的账号
String fromUserName=map.get("FromUserName");//发送消息用户的账号
String msgType=map.get("MsgType");//得到消息的类型
String content=map.get("Content");//得到文本消息的内容
/**
* 消息的类型:都可以在微信的开发者文档中找到
* 
*/
    public static final String MESSAGE_TEXT="text";//文本消息
    public static final String MESSAGE_VOICE="voice";//语音消息
    public static final String MESSAGE_MUSIC="music";//音乐
    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_IMAGE="image";//图片
    public static final String MESSAGE_EVENT="event";//事件
    public static final String MESSAGE_SUBSCRIBE="subscribe";//关注事件
    public static final String MESSAGE_UNSUBSCRIBE="unsubscribe";//取消关注事件
    public static final String MESSAGE_NEWS="news";//图文
    public static final String MESSAGE_CLICK = "CLICK";//菜单的点击事件
    public static final String MESSAGE_VIEW = "VIEW";//菜单的跳转
    public static final String MESSAGE_SCANCODE= "scancode_push";//扫描二维码事件
```

消息的回复
在公众号收到用户发送的消息后,进行消息的判断,然后在回复给用户,回复的格式也xml
回复消息是使用java对象进行转化,而且有些格式中的字段是都有的,所以可以生成一个父类,后面的各种类型的消息都继承这个父类

//文本消息的格式
<xml>
<ToUserName><![CDATA[toUser]]></ToUserName>
<FromUserName><![CDATA[fromUser]]></FromUserName>
<CreateTime>12345678</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[你好]]></Content>
</xml>
//父类
public class BaseMessage {
    private String ToUserName;// 给哪个账号
    private String FromUserName;// 来自哪个账号
    private long CreateTime;// 创建时间
    private String MsgType;// 消息类型
    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 long getCreateTime() {
        return CreateTime;
    }
    public void setCreateTime(long createTime) {
        CreateTime = createTime;
    }
    public String getMsgType() {
        return MsgType;
    }
    public void setMsgType(String msgType) {
        MsgType = msgType;
    }
}
//回复的文本信息
public class TextMessage extends BaseMessage{
    private String Content;// 文本内容
    private String MsgId;// 消息ID
    public String getContent() {
        return Content;
    }
    public void setContent(String content) {
        Content = content;
    }
    public String getMsgId() {
        return MsgId;
    }
    public void setMsgId(String msgId) {
        MsgId = msgId;
    }
    @Override
    public String toString() {
        return "TextMessage [Content=" + Content + ", MsgId=" + MsgId + "]";
    }

}

在创建了类之后,并且在要回复的信息封装好之后我们要将java对象转化成xml格式,这样要使用一个jar,帮助我们将java对象转化成xml格式的字符串:xstream-1.3.1.jar

    /**
     * 将文本对象转化为xml
     * @param textMessage
     * @return
     */
    public static String textMessageToXml(TextMessage baseMessage){
        XStream stream=new XStream();
        stream.alias("xml", baseMessage.getClass());
        return stream.toXML(baseMessage);
    }
    //封装回复的消息
    TextMessage message=new TextMessage();
    message.setFromUserName(toUserName);//这里填写的自己的账号,也就是前面的toUserName
    message.setToUserName(fromUserName);//填写给那个用户,也就是前面的fromUserName
    message.setMsgType("text");//消息的类型
    message.setCreateTime(new Date().getTime());//时间
    message.setContent("你好,欢迎关注我的微信公众号!");
    String xmlMessage=MessageUtils.textMessageToXml(message);//将java对象转化成xml格式的字符串
    if(xmlMessage!=null && !"".equals(xmlMessage)){
        out.print(xmlMessage);//输出字符串
    }
    out.flush();
    out.close();

这是测试的效果
这里写图片描述

2016-12-15 17:32:05 z69183787 阅读数 20045

第一步:配置微信公众号

进入微信公众平台地址

这里写图片描述

进入基本配置

这里写图片描述

这里需要填写的有服务器的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);
        }
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

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 "";
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141
  • 142
  • 143
  • 144
  • 145
  • 146
  • 147
  • 148
  • 149
  • 150
  • 151
  • 152
  • 153
  • 154
  • 155
  • 156
  • 157
  • 158
  • 159
  • 160
  • 161
  • 162
  • 163
  • 164
  • 165
  • 166
  • 167
  • 168
  • 169
  • 170
  • 171
  • 172
  • 173
  • 174
  • 175
  • 176
  • 177
  • 178
  • 179
  • 180
  • 181
  • 182
  • 183
  • 184
  • 185
  • 186
  • 187

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

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

这里写图片描述

下面是微信发过来的四个参数,以及给微信回复的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;
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34

用到的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();
    }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

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;

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

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

    }

}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126

用到的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>

如图所示

这里写图片描述

完整项目下载地址

https://github.com/peer44/testwechat

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

参考文献

微信开发者文档

2017-08-05 16:05:43 LJFPHP 阅读数 6831

最近开始做公司的微信项目,深切的感受到了微信的不同,做微信是需要从头开始好好学的,在此记录一下学习微信的过程,也希望以后能根据这些文章迅速掌握微信开发的知识。少踩坑,,(laravel框架)

1、首先需要进入微信公众号--》基本配置--》填上你的服务器信息--》验证TOKEN,获取appId和appkey

2、下载官方的SDK文件,参考文档:https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421140543

demo下载地址:http://www.cnblogs.com/txw1958/p/tutorials-of-wechat-public-platform.html       方倍工作室 的。

3、首先需要明确(1)验证token通过,证明你的服务器可以和微信进行交互

(2)进入微信页面,发送消息,此时,后台应接受消息,并判断消息类型

(3)根据消息类型,拼接不同的xml数据,返回给模板

(4)然后就实现了微信上自动回复的功能了。

4、


以上为验证token之后,在控制器设置的微信入口文件:这个文件也就是demo中的reponseMsg()方法:

主要用于获得微信发送过来的数据,然后再把tousername,fromusername等信息放到一起,提取用户发送消息的类型$event,并且请求下一个方法:

5、

这个方法主要是为了判断请求的类型,根据不同的类型会有不同的处理方法。在此只处理text类型的数据,并且调用text方法,获取拼接的数据。最后返回给响应方法handle_response()方法。

6、

在这里,我们把回复的内容都给写死了,大家到时候也完全可以从数据库读出这些数据。里面就是简单的拼接了一些图文信息,然后把这些信息返回。数组$arr是方便在模板上输出图文消息。

7、

该控制器主要是根据传过来的event不同,判断该调用哪个模板,该回复什么样的消息等。如果$event==text,则调用默认的模板:$xml = view(wechat.tpl_reply_text) ;如果$event为news,则需要返回图文消息,我们把数据都放到数组里面,在模板上输出即可。

8、

<xml>
  <ToUserName><![CDATA[{{$ToUserName}}]]></ToUserName>
  <FromUserName><![CDATA[{{$FromUserName}}]]></FromUserName>
  <CreateTime>{{$CreateTime}}</CreateTime>
  <MsgType><![CDATA[news]]></MsgType>
  <ArticleCount>{{$ArticleCount}}</ArticleCount>
  <Articles>
@foreach ($news  as $k => $v)
  <item>
  <Title><![CDATA[{{$v['Title']}}]]></Title>
  <Description><![CDATA[{{$v['Description']}}]]></Description>
  <PicUrl><![CDATA[{{$v['PicUrl']}}]]></PicUrl>
  <Url><![CDATA[{{$v['Url']}}]]></Url>
  </item>
@endforeach
  </Articles>
</xml>

这就是模板部分:直接输出即可。

9、在此已经是完成了微信的自动回复功能。用户输入指定的内容的时候,,我们也回复指定的内容回去。

微信官方的给的demo被拆分了,没有按照官方上那种返回xml数据,而是采用了laravel的模板来输出。


10、总结:虽然整体来说不够简洁,但也算是实现了功能,在此做个记录。以后水平高了再完善完善。

2019-09-20 18:16:29 qq_22770457 阅读数 6953

首先本文的测试微信版本是7.0.3 ,亲测可以使用。

 

需要实现-抓取微信自动回复消息的功能点。

一.首先打开DDMS,使用按钮。

在微信中回复一个消息

点击Stop Method Profiling。

二.查看生成的报表,观察到如下两个方法

其中1应该是发送消息的接口方法。2应该是UI层显示的方法。

 

三.首先分析第一个方法:

1.,可以看到,参数值是String,返回值是Boolean(Z表示boolean),这里测试hook一下该函数。

2.测试结果:

可以看到成功hook到了,说明发送消息是调用的该方法。

3.然后如果需要调用该方法,首先需要获得该方法的实例,并去调用,然后还需要获取到具体消息指向的发送者。

那么这里需要对源码进行分析。

找到Pg方法

调用了apl方法,这里传入了str,继续往下分析可以看到调用了这个方法:

 

可以看到这个ex方法应该是具体的发送消息的方法:

其中调用了

setContent方法即为具体的设置方法,

可以看到该类cx就是消息的包装类:

 

这里通过id参数入手,field_talkerId,hook使用到该id的方法,打印堆栈信息:

com.tencent.mm.g.c.cx.hm

Class clz = XposedHelpers.findClass("com.tencent.mm.g.c.cx", lpparam.classLoader);

XposedHelpers.findAndHookMethod(clz, "hm", int.class, new XC_MethodHook() {

@Override

protected void beforeHookedMethod(MethodHookParam param) throws Throwable {

super.beforeHookedMethod(param);

LogUtil.d("打印调用堆栈开始...");

LogUtil.logStackTraces();

LogUtil.d("打印调用堆栈结束...");

}

});

测试输出结果:

进入该方法:

这里hook测试了一下,我发现原来field_talker才是真正的微信id, field_talkerId并没有用到。

 

所以整个hook的关键点方法都已经找到了:

1. com.tencent.mm.ui.chatting.p.Pg(String str) 方法,参数就是消息内容,可以发送消息,但是无法设置接受者id

 

2. com.tencent.mm.modelmulti.h(String str, String str2, int i, int i2, Object obj) 构造方法,第一个参数是接收者的微信id,第二个参数是内容。

 

自动回复的思路:

首先获得发送者的id,并保存下来,然后调用hook上面第一个方法发送消息,hook第二个方法修改微信id,实现收到消息立刻自动回复。

 

 

四:难点:调用发送接口进行发送:

1.回到之前分析的具体发送的方法,结合消息体用到的地方,可以观察到:

这里最终是调用了这个a方法,拼接参数进行发送的。a方法内部:

可以看到两个参数,第一个就是消息体封装类。

最后按步骤调用 

 

测试结果,大功告成:

 

 

 

 

2018-04-07 21:13:33 gqycrush 阅读数 586

微信,大家应该都不陌生了,应该说每个人都离不开微信,它不仅仅是社交软件,还是手机支付工具,而且现在的公众号更是微信的一大特色,他可以对外宣传和介绍自己的产品,作为一名IT,我们不仅要会使用微信,还要进行微信的二次开发,接下来就给大家介绍一下微信开发的入门。

开发者首先要接入微信公众平台,这就需要我们编写代码来给两者开通一条道路:

$echoStr = $_GET["echostr"];

仅此一条就可以开通,但是这并不安全,以下是完整代码:

<?php
/**
  * wechat php test
  */
//define your token
define("TOKEN", "weixin");
$wechatObj = new wechatCallbackapiTest();
$wechatObj->valid();

class wechatCallbackapiTest
{
	public function valid()
    {
    	if($this->checkSignature()==fslse){
    		die('非法请求');
    		exit();
    	}
        
        //valid signature , option
        if(isset($_GET["echostr"])){
        	$echoStr = $_GET["echostr"];
        	echo $echoStr;
        	exit();
        }else{
        	$this->responseMsg();
        }


        // if($this->checkSignature()){
        // 	echo $echoStr;
        // 	exit;
        // }
    }
    //自动回复
    public function responseMsg()
    {
		//得到微信向公众号发送的消息接受消息
		// $postStr = $GLOBALS["HTTP_RAW_POST_DATA"];
		//存xml文件,tousername--微信公众号 fromusername--openid cratetime--是时间戳 <![]]>为了防止有特殊字符,里面的是数据,不是节点
		$postStr=file_get_contents("php://input");
		file_put_contents("msg.txt", $postStr,FILE_APPEND);
		// $postObj=simplexml_load_string($postStr,'SimpleXMLElement',E)


      	//回复消息
		if (!empty($postStr)){
                
              	$postObj = simplexml_load_string($postStr, 'SimpleXMLElement', LIBXML_NOCDATA);
                $fromUsername = $postObj->FromUserName;
                $toUsername = $postObj->ToUserName;
                $keyword = trim($postObj->Content);
                $time = time();
                //xml模板%s占位符
                $textTpl = "<xml>
							<ToUserName><![CDATA[%s]]></ToUserName>
							<FromUserName><![CDATA[%s]]></FromUserName>
							<CreateTime>%s</CreateTime>
							<MsgType><![CDATA[%s]]></MsgType>
							<Content><![CDATA[%s]]></Content>
							<FuncFlag>0</FuncFlag>
							</xml>";             
				if(!empty( $keyword ))
                {
                	//sprintf---返回一个字符串不做输出
              		$msgType = "text";
                	$contentStr = "欢迎使用测试账号!";
                	$resultStr = sprintf($textTpl, $fromUsername, $toUsername, $time, $msgType, $contentStr);
                	echo $resultStr;
                }else{
                	echo "Input something...";
                }


        }else {
        	echo "";
        	exit;
        }
    }

	//判断是否是腾讯提交,为了更加安全
	private function checkSignature()
	{
//将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
        $signature = $_GET["signature"];
        $timestamp = $_GET["timestamp"];
        $nonce = $_GET["nonce"];	
        		
		$token = TOKEN;
		$tmpArr = array($token, $timestamp, $nonce);
		sort($tmpArr);
		$tmpStr = implode( $tmpArr );
		$tmpStr = sha1( $tmpStr );
		
		if( $tmpStr == $signature ){
			return true;
		}else{
			return false;
		}
	}
}
?>

以上的代码放在其阿里云服务器里就可以打开你的微信,随便输入文字内容,就有回复了。

下面一个简单的关键字回复的例子,只需新添加一个方法:

//关键字回复
        public function say($news){
            if($news == "天气"){
                $content="今天天气阴天";
                return $content;
            }else if($news == "放假吗"){
                $content="清明放三天假";
                return $content;
            }else{
                $content="这个问题不知道怎么回复您!";
               return $content;
            }
        }

在回复消息方法里调用此方法就可以在微信里输入关键字来得到相应的回复了,大家学会了吗,赶紧打开电脑试一下吧。