赞
踩
开发第一步,微信服务器要验证服务器是否有效。登录微信公众平台,在开发-基本设置页面,服务器配置项填写: 服务器地址(URL) 、令牌(Token) 、消息加解密密钥(EncodingAESKey)
针对URL开发两个接口:
GET请求接口:用来做服务器有效性验证
POST请求接口:用来接收微信服务器发送来的消息。
Token可以任意填写,用作生成签名
EncodingAESKey手动填写或随机生成,用作消息体加解密密钥。
现在提交肯定是验证token失败,因为还需要完成代码逻辑。
这里使用NATAPP内网穿透,文档齐全,很简单。
<!--xml解析-->
<dependency>
<groupId>org.dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>2.1.3</version>
</dependency>
<!--对象转xml-->
<dependency>
<groupId>com.thoughtworks.xstream</groupId>
<artifactId>xstream</artifactId>
<version>1.4.10</version>
</dependency>
@RestController @RequestMapping("/WeChat") public class WxController { //与微信公众号 开发 基本配置中配置token保持一致 private static final String TOKEN = "token"; private static final char[] HEX_DIGITS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'}; /** * 服务器校验 * 安全校验,标识请求是否来源于微信 * * @param request * @param response * @throws UnsupportedEncodingException */ @GetMapping("/wx") public void verifyToken(HttpServletRequest request, HttpServletResponse response) throws UnsupportedEncodingException { request.setCharacterEncoding("UTF-8"); //微信加密签名 String signature = request.getParameter("signature"); //时间戳 String timestamp = request.getParameter("timestamp"); //随机数 String nonce = request.getParameter("nonce"); //随机字符串 String echostr = request.getParameter("echostr"); PrintWriter out = null; try { out = response.getWriter(); if (checkSignature(signature, timestamp, nonce)) { out.write(echostr); } } catch (IOException e) { e.printStackTrace(); } finally { out.close(); } } /** * 校验微信签名 * * @param signature * @param timestamp * @param nonce * @return */ private static boolean checkSignature(String signature, String timestamp, String nonce) { String[] str = new String[]{TOKEN, timestamp, nonce}; //对token、timestamp、nonce三个参数进行字典序排序 Arrays.sort(str); //将三个参数字符串拼接成一个字符串进行sha1加密 StringBuffer buffer = new StringBuffer(); for (int i = 0; i < str.length; i++) { buffer.append(str[i]); } String temp = encode(buffer.toString()); //获得加密后的字符串与signature对比 return signature.equals(temp); } /** * 对字符串进行加密 * * @param str * @return */ private static String encode(String str) { if (str == null) { return null; } MessageDigest messageDigest = null; try { messageDigest = MessageDigest.getInstance("SHA1"); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } messageDigest.update(str.getBytes()); byte[] digest = messageDigest.digest(); int len = digest.length; StringBuilder buf = new StringBuilder(len * 2); for (int j = 0; j < len; j++) { buf.append(HEX_DIGITS[(digest[j] >> 4) & 0x0f]); buf.append(HEX_DIGITS[digest[j] & 0x0f]); } return buf.toString(); } }
消息接收接口和服务器校验接口地址是一样的,只不过消息接收接口是一个 POST 请求。由于公众号后台配置消息加解密方式:明文模式,在后台收到的消息可以直接处理。
1.收到微信服务器发来的消息之后,进行XML解析,提取出需要的信息。
2.根据发送的消息类型或业务做相关操作,再将操作的结果返回给微信服务器。
1.接受到的文本消息格式
<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 | 是 | 开发者微信号 |
FromUserName | 是 | 发送方帐号(一个OpenID) |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,文本为text |
Content | 是 | 文本消息内容 |
MsgId | 是 | 消息id,64位整型 |
2.回复文本消息格式
收到粉丝消息后不想或者不能5秒内回复时,需回复“success”字符串
<xml>
<ToUserName><![CDATA[粉丝号]]></ToUserName>
<FromUserName><![CDATA[公众号]]></FromUserName>
<CreateTime>1460541339</CreateTime>
<MsgType><![CDATA[text]]></MsgType>
<Content><![CDATA[test]]></Content>
</xml>
参数 | 是否必须 | 描述 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | 消息类型,文本为text |
Content | 是 | 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示) |
/** * 接受微信发送的消息 * * @param request * @param response * @throws Exception */ @PostMapping("/wx") public void processTheMessage(HttpServletRequest request, HttpServletResponse response) throws Exception { Map<String, String> parseXml = parseXml(request); /** * 接受到的文本消息格式 * <xml> * <ToUserName><![CDATA[公众号]]></ToUserName> * <FromUserName><![CDATA[粉丝号]]></FromUserName> * <CreateTime>1460537339</CreateTime> * <MsgType><![CDATA[text]]></MsgType> * <Content><![CDATA[欢迎开启公众号开发者模式]]></Content> * <MsgId>6272960105994287618</MsgId> * </xml> */ //开发者微信号 String tousername = parseXml.get("ToUserName"); //发送方帐号(一个OpenID) String fromusername = parseXml.get("FromUserName"); //消息创建时间 (整型) String createTime = parseXml.get("CreateTime"); //消息类型,文本为text String msgType = parseXml.get("MsgType"); //文本消息内容 String content = parseXml.get("Content"); //消息id,64位整型 String msgId = parseXml.get("MsgId"); System.out.println("开发者微信号: " + tousername); System.out.println("发送方帐号: " + fromusername); System.out.println("消息创建时间: " + createTime); System.out.println("消息类型: " + msgType); System.out.println("文本消息内容: " + content); System.out.println("消息id: " + msgId); /** * 被动回复文本消息的格式 * <xml> * <ToUserName><![CDATA[粉丝号]]></ToUserName> * <FromUserName><![CDATA[公众号]]></FromUserName> * <CreateTime>1460541339</CreateTime> * <MsgType><![CDATA[text]]></MsgType> * <Content><![CDATA[test]]></Content> * </xml> * * 需注意ToUserName与FromUserName的设值 */ Message message = new Message(); message.setToUserName(fromusername); message.setFromUserName(tousername); message.setCreateTime(createTime); message.setMsgType(msgType); message.setContent("Hello 管理员。"); WxController.xstream.alias("xml", Message.class); String xml = WxController.xstream.toXML(message); System.out.println(xml); request.setCharacterEncoding("UTF-8"); response.setCharacterEncoding("UTF-8"); PrintWriter out = response.getWriter(); out.println(xml); out.flush(); } /** * 解析微信发来的请求(XML) * * @param request * @return * @throws Exception */ private static Map<String, String> parseXml(HttpServletRequest request) throws Exception { // 将解析结果存储在HashMap中 Map<String, String> map = new HashMap<String, String>(); // 从request中取得输入流 InputStream inputStream = request.getInputStream(); // 读取输入流 SAXReader reader = new SAXReader(); Document document = reader.read(inputStream); //得到xml根元素 Element root = document.getRootElement(); // 得到根元素的所有子节点 List<Element> elementList = root.elements(); // 遍历所有子节点 for (Element e : elementList) { map.put(e.getName(), e.getText()); } // 释放资源 inputStream.close(); inputStream = null; return map; } /** * 扩展xstream使其支持CDATA */ private static XStream xstream = new XStream(new XppDriver() { @Override public HierarchicalStreamWriter createWriter(Writer out) { return new PrettyPrintWriter(out) { // 对所有xml节点的转换都增加CDATA标记 boolean cdata = true; @Override public void startNode(String name, Class clazz) { super.startNode(name, clazz); } @Override protected void writeText(QuickWriter writer, String text) { if (cdata) { writer.write("<![CDATA["); writer.write(text); writer.write("]]>"); } else { writer.write(text); } } }; } });
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。