破晓 发表于 2016-8-18 16:04:21

微信公众平台 java示例 接收消息并回复

来源:http://blog.csdn.net/morning99/article/details/43865471

最近公司在开发微信项目,所以自己也试着申请了个人的订阅服务号,实现了通过微信接收信息转发至java后台解析并回复的消息的简单功能,在还没忘记的时候记录一下,以便日后查阅,并且贡献出代码希望能给大家一个参考。好首先你要看下面的示例,要事先申请微信公众平台的订阅服务号(个人只能申请这个),地址https://mp.weixin.qq.com ,申请的范例我这里就不讲了,一般根据提示可以自行完成,如果这都完成不了,那只能去度娘翻翻了。要想让用户发送给公众帐号的消息转发给java后台服务器,首先要 在开发者中心 进行 服务器配置 ,下图为认证启动后小效果:
你要先进入到 修改配置里面,如下图:
你要填写这几个文本框内的内容,1.URL 不用解释了,就是微信将用户发来的消息转发到你服务器的请求的地址,我让微信把请求发送到本地服务这样方便调试。推荐一款反向代理的工具 pagekite.net ,感兴趣的朋友可以去搜索一下。使用相当方便,就是需要python2.7.x环境支持,然后运行下载的一个脚本,输入你的邮箱,然后在输入你要设置的域名前缀,就搞定,下次运行就不用在输入,它影射的是本地80端口,所以你启动服务的时候记得改成80端口就对了,还有这个貌似对于一个邮箱只有31天和5个连接的限制,PS:邮箱这东西都是免费的,你懂的。
2.Token:这个长度符合就行 自己随意3.EncodingAESKey:点击随机生成 就OK
下面介绍我的代码:我的开发环境是eclipse+springMvc"/chat" 是我最终项目的请求Controller URL路径
下面是homecontroller
@Controller
@RequestMapping("/*")
public class HomeController {

        private String Token = "123456789abcdef";

        @RequestMapping(value = "chat", method = { RequestMethod.GET, RequestMethod.POST })
        @ResponseBody
        public void liaotian(Model model, HttpServletRequest request, HttpServletResponse response) {
                System.out.println("进入chat");
                boolean isGet = request.getMethod().toLowerCase().equals("get");
                if (isGet) {
                        String signature = request.getParameter("signature");
                        String timestamp = request.getParameter("timestamp");
                        String nonce = request.getParameter("nonce");
                        String echostr = request.getParameter("echostr");
                        System.out.println(signature);
                        System.out.println(timestamp);
                        System.out.println(nonce);
                        System.out.println(echostr);
                        access(request, response);
                } else {
                        // 进入POST聊天处理
                        System.out.println("enter post");
                        try {
                                // 接收消息并返回消息
                                acceptMessage(request, response);
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
                }
        }

        /**
       * 验证URL真实性
       *
       * @author morning
       * @date 2015年2月17日 上午10:53:07
       * @param request
       * @param response
       * @return String
       */
        private String access(HttpServletRequest request, HttpServletResponse response) {
                // 验证URL真实性
                System.out.println("进入验证access");
                String signature = request.getParameter("signature");// 微信加密签名
                String timestamp = request.getParameter("timestamp");// 时间戳
                String nonce = request.getParameter("nonce");// 随机数
                String echostr = request.getParameter("echostr");// 随机字符串
                List<String> params = new ArrayList<String>();
                params.add(Token);
                params.add(timestamp);
                params.add(nonce);
                // 1. 将token、timestamp、nonce三个参数进行字典序排序
                Collections.sort(params, new Comparator<String>() {
                        @Override
                        public int compare(String o1, String o2) {
                                return o1.compareTo(o2);
                        }
                });
                // 2. 将三个参数字符串拼接成一个字符串进行sha1加密
                String temp = SHA1.encode(params.get(0) + params.get(1) + params.get(2));
                if (temp.equals(signature)) {
                        try {
                                response.getWriter().write(echostr);
                                System.out.println("成功返回 echostr:" + echostr);
                                return echostr;
                        } catch (IOException e) {
                                e.printStackTrace();
                        }
                }
                System.out.println("失败 认证");
                return null;
        }

        private void acceptMessage(HttpServletRequest request, HttpServletResponse response) throws IOException {
                // 处理接收消息
                ServletInputStream in = request.getInputStream();
                // 将POST流转换为XStream对象
                XStream xs = SerializeXmlUtil.createXstream();
                xs.processAnnotations(InputMessage.class);
                xs.processAnnotations(OutputMessage.class);
                // 将指定节点下的xml节点数据映射为对象
                xs.alias("xml", InputMessage.class);
                // 将流转换为字符串
                StringBuilder xmlMsg = new StringBuilder();
                byte[] b = new byte;
                for (int n; (n = in.read(b)) != -1;) {
                        xmlMsg.append(new String(b, 0, n, "UTF-8"));
                }
                // 将xml内容转换为InputMessage对象
                InputMessage inputMsg = (InputMessage) xs.fromXML(xmlMsg.toString());

                String servername = inputMsg.getToUserName();// 服务端
                String custermname = inputMsg.getFromUserName();// 客户端
                long createTime = inputMsg.getCreateTime();// 接收时间
                Long returnTime = Calendar.getInstance().getTimeInMillis() / 1000;// 返回时间

                // 取得消息类型
                String msgType = inputMsg.getMsgType();
                // 根据消息类型获取对应的消息内容
                if (msgType.equals(MsgType.Text.toString())) {
                        // 文本消息
                        System.out.println("开发者微信号:" + inputMsg.getToUserName());
                        System.out.println("发送方帐号:" + inputMsg.getFromUserName());
                        System.out.println("消息创建时间:" + inputMsg.getCreateTime() + new Date(createTime * 1000l));
                        System.out.println("消息内容:" + inputMsg.getContent());
                        System.out.println("消息Id:" + inputMsg.getMsgId());

                        StringBuffer str = new StringBuffer();
                        str.append("<xml>");
                        str.append("<ToUserName><!]></ToUserName>");
                        str.append("<FromUserName><!]></FromUserName>");
                        str.append("<CreateTime>" + returnTime + "</CreateTime>");
                        str.append("<MsgType><!]></MsgType>");
                        str.append("<Content><!]></Content>");
                        str.append("</xml>");
                        System.out.println(str.toString());
                        response.getWriter().write(str.toString());
                }
                // 获取并返回多图片消息
                if (msgType.equals(MsgType.Image.toString())) {
                        System.out.println("获取多媒体信息");
                        System.out.println("多媒体文件id:" + inputMsg.getMediaId());
                        System.out.println("图片链接:" + inputMsg.getPicUrl());
                        System.out.println("消息id,64位整型:" + inputMsg.getMsgId());

                        OutputMessage outputMsg = new OutputMessage();
                        outputMsg.setFromUserName(servername);
                        outputMsg.setToUserName(custermname);
                        outputMsg.setCreateTime(returnTime);
                        outputMsg.setMsgType(msgType);
                        ImageMessage images = new ImageMessage();
                        images.setMediaId(inputMsg.getMediaId());
                        outputMsg.setImage(images);
                        System.out.println("xml转换:/n" + xs.toXML(outputMsg));
                        response.getWriter().write(xs.toXML(outputMsg));

                }
        }

}
加密SHA1,此代码是来自网上

/*
* 微信公众平台(JAVA) SDK
*
* Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.
* http://www.ansitech.com/weixin/sdk/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mor.util;

import java.security.MessageDigest;

/**
* <p>
* Title: SHA1算法
* </p>
*
*/
public final class SHA1 {

        private static final char[] HEX_DIGITS = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };

        /**
       * Takes the raw bytes from the digest and formats them correct.
       *
       * @param bytes
       *            the raw bytes from the digest.
       * @return the formatted bytes.
       */
        private static String getFormattedText(byte[] bytes) {
                int len = bytes.length;
                StringBuilder buf = new StringBuilder(len * 2);
                // 把密文转换成十六进制的字符串形式
                for (int j = 0; j < len; j++) {
                        buf.append(HEX_DIGITS[(bytes >> 4) & 0x0f]);
                        buf.append(HEX_DIGITS & 0x0f]);
                }
                return buf.toString();
        }

        public static String encode(String str) {
                if (str == null) {
                        return null;
                }
                try {
                        MessageDigest messageDigest = MessageDigest.getInstance("SHA1");
                        messageDigest.update(str.getBytes());
                        return getFormattedText(messageDigest.digest());
                } catch (Exception e) {
                        throw new RuntimeException(e);
                }
        }
}
输入信息实体类 InputMessage
<pre name="code" class="java">/*
* 微信公众平台(JAVA) SDK
*
* Copyright (c) 2014, Ansitech Network Technology Co.,Ltd All rights reserved.
* http://www.ansitech.com/weixin/sdk/
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.mor.maven.demo.mavenweb.model;

import java.io.Serializable;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
* POST的XML数据包转换为消息接受对象
*
* <p>
* 由于POST的是XML数据包,所以不确定为哪种接受消息,<br/>
* 所以直接将所有字段都进行转换,最后根据<tt>MsgType</tt>字段来判断取何种数据
* </p>
*
*/
@XStreamAlias("xml")
public class InputMessage implements Serializable {

        /**
       *
       */
        private static final long serialVersionUID = 1L;
        @XStreamAlias("ToUserName")
        private String ToUserName;
        @XStreamAlias("FromUserName")
        private String FromUserName;
        @XStreamAlias("CreateTime")
        private Long CreateTime;
        @XStreamAlias("MsgType")
        private String MsgType = "text";
        @XStreamAlias("MsgId")
        private Long MsgId;
        // 文本消息
        @XStreamAlias("Content")
        private String Content;
        // 图片消息
        @XStreamAlias("PicUrl")
        private String PicUrl;
        // 位置消息
        @XStreamAlias("LocationX")
        private String LocationX;
        @XStreamAlias("LocationY")
        private String LocationY;
        @XStreamAlias("Scale")
        private Long Scale;
        @XStreamAlias("Label")
        private String Label;
        // 链接消息
        @XStreamAlias("Title")
        private String Title;
        @XStreamAlias("Description")
        private String Description;
        @XStreamAlias("Url")
        private String URL;
        // 语音信息
        @XStreamAlias("MediaId")
        private String MediaId;
        @XStreamAlias("Format")
        private String Format;
        @XStreamAlias("Recognition")
        private String Recognition;
        // 事件
        @XStreamAlias("Event")
        private String Event;
        @XStreamAlias("EventKey")
        private String EventKey;
        @XStreamAlias("Ticket")
        private String Ticket;

        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 Long getMsgId() {
                return MsgId;
        }

        public void setMsgId(Long msgId) {
                MsgId = msgId;
        }

        public String getContent() {
                return Content;
        }

        public void setContent(String content) {
                Content = content;
        }

        public String getPicUrl() {
                return PicUrl;
        }

        public void setPicUrl(String picUrl) {
                PicUrl = picUrl;
        }

        public String getLocationX() {
                return LocationX;
        }

        public void setLocationX(String locationX) {
                LocationX = locationX;
        }

        public String getLocationY() {
                return LocationY;
        }

        public void setLocationY(String locationY) {
                LocationY = locationY;
        }

        public Long getScale() {
                return Scale;
        }

        public void setScale(Long scale) {
                Scale = scale;
        }

        public String getLabel() {
                return Label;
        }

        public void setLabel(String label) {
                Label = label;
        }

        public String getTitle() {
                return Title;
        }

        public void setTitle(String title) {
                Title = title;
        }

        public String getDescription() {
                return Description;
        }

        public void setDescription(String description) {
                Description = description;
        }

        public String getURL() {
                return URL;
        }

        public void setURL(String uRL) {
                URL = uRL;
        }

        public String getEvent() {
                return Event;
        }

        public void setEvent(String event) {
                Event = event;
        }

        public String getEventKey() {
                return EventKey;
        }

        public void setEventKey(String eventKey) {
                EventKey = eventKey;
        }

        public String getMediaId() {
                return MediaId;
        }

        public void setMediaId(String mediaId) {
                MediaId = mediaId;
        }

        public String getFormat() {
                return Format;
        }

        public void setFormat(String format) {
                Format = format;
        }

        public String getRecognition() {
                return Recognition;
        }

        public void setRecognition(String recognition) {
                Recognition = recognition;
        }

        public String getTicket() {
                return Ticket;
        }

        public void setTicket(String ticket) {
                Ticket = ticket;
        }
}
为了加入 CDATA 验证创建的@interface类
package com.mor.maven.demo.mavenweb.model;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.FIELD })
public @interface XStreamCDATA {

}改写的XStream工具类
package com.mor.util;

import java.io.Writer;
import java.lang.reflect.Field;

import com.mor.maven.demo.mavenweb.model.XStreamCDATA;
import com.thoughtworks.xstream.XStream;
import com.thoughtworks.xstream.annotations.XStreamAlias;
import com.thoughtworks.xstream.core.util.QuickWriter;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;
import com.thoughtworks.xstream.io.xml.PrettyPrintWriter;
import com.thoughtworks.xstream.io.xml.XppDriver;

/**
* xml 转换工具类
*
* @author morning
* @date 2015年2月16日 下午2:42:50
*/
public class SerializeXmlUtil {

        public static XStream createXstream() {
                return new XStream(new XppDriver() {
                        @Override
                        public HierarchicalStreamWriter createWriter(Writer out) {
                                return new PrettyPrintWriter(out) {
                                        boolean cdata = false;
                                        Class<?> targetClass = null;

                                        @Override
                                        public void startNode(String name, @SuppressWarnings("rawtypes") Class clazz) {
                                                super.startNode(name, clazz);
                                                // 业务处理,对于用XStreamCDATA标记的Field,需要加上CDATA标签
                                                if (!name.equals("xml")) {
                                                        cdata = needCDATA(targetClass, name);
                                                } else {
                                                        targetClass = clazz;
                                                }
                                        }

                                        @Override
                                        protected void writeText(QuickWriter writer, String text) {
                                                if (cdata) {
                                                        writer.write("<![CDATA[");
                                                        writer.write(text);
                                                        writer.write("]]>");
                                                } else {
                                                        writer.write(text);
                                                }
                                        }
                                };
                        }
                });
        }

        private static boolean needCDATA(Class<?> targetClass, String fieldAlias) {
                boolean cdata = false;
                // first, scan self
                cdata = existsCDATA(targetClass, fieldAlias);
                if (cdata)
                        return cdata;
                // if cdata is false, scan supperClass until java.lang.Object
                Class<?> superClass = targetClass.getSuperclass();
                while (!superClass.equals(Object.class)) {
                        cdata = existsCDATA(superClass, fieldAlias);
                        if (cdata)
                                return cdata;
                        superClass = superClass.getClass().getSuperclass();
                }
                return false;
        }

        private static boolean existsCDATA(Class<?> clazz, String fieldAlias) {
                if ("MediaId".equals(fieldAlias)) {
                        return true; // 特例添加 morning99
                }
                // scan fields
                Field[] fields = clazz.getDeclaredFields();
                for (Field field : fields) {
                        // 1. exists XStreamCDATA
                        if (field.getAnnotation(XStreamCDATA.class) != null) {
                                XStreamAlias xStreamAlias = field.getAnnotation(XStreamAlias.class);
                                // 2. exists XStreamAlias
                                if (null != xStreamAlias) {
                                        if (fieldAlias.equals(xStreamAlias.value()))// matched
                                                return true;
                                } else {// not exists XStreamAlias
                                        if (fieldAlias.equals(field.getName()))
                                                return true;
                                }
                        }
                }
                return false;
        }

}
输出实体类 OutputMessage
package com.mor.maven.demo.mavenweb.model;

import com.thoughtworks.xstream.annotations.XStreamAlias;

/**
*
* @author morning
* @date 2015年2月16日 下午2:29:32
*/
@XStreamAlias("xml")
public class OutputMessage {

        @XStreamAlias("ToUserName")
        @XStreamCDATA
        private String ToUserName;

        @XStreamAlias("FromUserName")
        @XStreamCDATA
        private String FromUserName;

        @XStreamAlias("CreateTime")
        private Long CreateTime;

        @XStreamAlias("MsgType")
        @XStreamCDATA
        private String MsgType = "text";

        private ImageMessage Image;

        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 ImageMessage getImage() {
                return Image;
        }

        public void setImage(ImageMessage image) {
                Image = image;
        }

}
图片信息实体类
package com.mor.maven.demo.mavenweb.model;

import com.thoughtworks.xstream.annotations.XStreamAlias;

@XStreamAlias("Image")
public class ImageMessage extends MediaIdMessage {
}多媒体id 实体类
package com.mor.maven.demo.mavenweb.model;

import com.thoughtworks.xstream.annotations.XStreamAlias;

public class MediaIdMessage {
        @XStreamAlias("MediaId")
        @XStreamCDATA
        private String MediaId;

        public String getMediaId() {
                return MediaId;
        }

        public void setMediaId(String mediaId) {
                MediaId = mediaId;
        }

}
基本就这些类,也不知道拷贝全没有。不过在输出xml的时候由于要添加CDATA标签所以没有实现完美,目前自己在SerializeXmlUtil 内添加了一下判断
如果是子标签下的值目前只能用这种方法加CDATA,不知道各位同学有没有好的方法。目前只是实现了服务器认证,接收文本信息并回复原文本信息加上些附加信息,接收图片信息并返回原图片信息。后期会有扩展,先记录到此。

依赖的类库(jar包)


源码:

我是一个兵 发表于 2016-8-18 17:24:40

看看。。。。。
页: [1]
查看完整版本: 微信公众平台 java示例 接收消息并回复