本文采用开发语言为Java,使用spring mvc框架
1.申请微信账号
目前微信账号有服务号、订阅号、企业号三种,根据微信官方的解释这三种的的服务类型有所区别。
服务号:给企业和组织提供更强大的业务服务与用户管理能力,帮助企业快速实现全新的公众号服务平台。
订阅号:为媒体和个人提供一种新的信息传播方式,构建与读者之间更好的沟通与管理模式。
企业号:帮助企业和组织内部建立员工、上下游合作伙伴与企业IT系统间的连接。
对于个人只允许申请订阅号,不过对于微信的开发人员,微信提供了更方便的测试平台。微信公众平台接口,开发人员通过手机微信扫二维码就可以申请测试账号。
完成注册之后,会进入测试页面,之后每次通过微信扫码可登录。
2.配置微信服务器地址
登录微信公众平台官网后,在测试号管理页面填写服务器地址URL、Token和,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名。在正式号中还有EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。
①、验证服务器地址的有效性,开发者提交信息后,微信服务器将发送GET请求到填写的服务器地址URL上,GET请求携带四个参数:
参数 | 描述 |
signature |
信加密签名,signature结合了开发者填写的token 参数和请求中的timestamp参数、nonce参数。 |
timestamp | 时间戳 |
nonce | 随机数 |
echostr | 随机字符串 |
开发者通过检验signature对请求进行校验。若确认此次GET请求来自微信服务器,请原样返回echostr参数内容,则接入生效,成为开发者成功,否则接入失败。
加密/校验流程如下:
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
校验有效性代码如下:
public static boolean checkSignature(String signature, String timestamp, String nonce,String token) { String[] arr = new String[]{token, timestamp, nonce}; // 将token、timestamp、nonce三个参数进行字典序排序 Arrays.sort(arr); StringBuilder content = new StringBuilder(); for (int i = 0; i < arr.length; i++) { content.append(arr[i]); } MessageDigest md = null; String tmpStr = null; try { md = MessageDigest.getInstance("SHA-1"); // 将三个参数字符串拼接成一个字符串进行sha1加密 byte[] digest = md.digest(content.toString().getBytes()); tmpStr = byteToStr(digest); } catch (NoSuchAlgorithmException e) { e.printStackTrace(); } content = null; // 将sha1加密后的字符串可与signature对比,标识该请求来源于微信 return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false; }
完整代码如下:
@RequestMapping("/weixin") public void weixin(HttpServletRequest req, HttpServletResponse resp) { PrintWriter printWriter = null; String respMessage = null; try { req.setCharacterEncoding("UTF-8"); resp.setCharacterEncoding("UTF-8"); String method = req.getMethod(); // 微信加密签名 String signature = req.getParameter("signature"); // 时间戳 String timestamp = req.getParameter("timestamp"); // 随机数 String nonce = req.getParameter("nonce"); // 随机字符串 String echostr = req.getParameter("echostr"); if (StringUtils.isNullOrEmpty(signature) || StringUtils.isNullOrEmpty(timestamp) || StringUtils.isNullOrEmpty(nonce)) { logger.error("Invalid request."); return; } // 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败 if (SignUtil.checkSignature(signature, timestamp, nonce, "weixin")) { respMessage = echostr; } if ("POST".equals(method)) { // 调用核心业务类接收消息、处理消息 respMessage = weixinMessageDispatcher.processRequest(req); } printWriter = resp.getWriter(); if (respMessage != null) { printWriter.write(respMessage); } } catch (Exception e) { logger.error("Failed to process request.", e); } }
②、接入微信公众号
完成以上代码之后,就可以把我们的网站接入微信公众号。因为微信公众号需要访问我们的服务器,所有我们的服务必须在公网上能够访问。
作为开发人员我们一般使用Ngrok来完成内网穿透,开发测试比较方便,网上教程一大把或者注册个新浪SAE(不过最近云豆消耗好像变多了,java耗云豆较多,php基本不损耗)。启动Ngrok。可以写个批处理方便一键启动。
call E:\软件\开发工具\Java\ngrok\ngrok http -subdomain=min 8080
启动结果:成功之后我们可以通过http://min.ngrok.io访问本地8080端口
登陆微信公众号测试平台,输入URL和token,单击提交。
3.接收和回复消息
①、接收消息,?当普通微信用户向公众账号发消息时,微信服务器将POST消息的XML数据包到开发者填写的URL上。消息格式如下
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>1348831860</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[test]]></Content> <MsgId>1234567890123456</MsgId> </xml>
参数 | 描述 |
---|---|
ToUserName | 开发者微信号 |
FromUserName | 发送方帐号(一个OpenID) |
CreateTime | 消息创建时间 (整型) |
MsgType | text |
Content | 文本消息内容 |
MsgId | 消息id,64位整型 |
②、回复消息,当服务器接收到一个POST请求,我们可以在响应包(Get)中返回特定XML结构,来对该消息进行响应回复。
<xml> <ToUserName><![CDATA[toUser]]></ToUserName> <FromUserName><![CDATA[fromUser]]></FromUserName> <CreateTime>12345678</CreateTime> <MsgType><![CDATA[text]]></MsgType> <Content><![CDATA[你好]]></Content> </xml>
参数 | 是否必须 | 描述 |
---|---|---|
ToUserName | 是 | 接收方帐号(收到的OpenID) |
FromUserName | 是 | 开发者微信号 |
CreateTime | 是 | 消息创建时间 (整型) |
MsgType | 是 | text |
Content | 是 | 回复的消息内容(换行:在content中能够换行,微信客户端就支持换行显示) |
实现代码:
public String processRequest(HttpServletRequest request) { String respMessage = null; try { // 默认返回的文本消息内容 String respContent = "Help"; // xml请求解析 Map<String, String> requestMap = MessageUtil.parseXml(request); // 打印报文 logger.info("requestMessage map is :" + requestMap.toString()); // 发送方帐号(open_id) String fromUserName = requestMap.get("FromUserName"); // 公众帐号 String toUserName = requestMap.get("ToUserName"); // 消息类型 String msgType = requestMap.get("MsgType"); // 消息内容 String content = requestMap.get("Content"); // 返回信息 TextMessage textMessage = new TextMessage(); textMessage.setToUserName(fromUserName); textMessage.setFromUserName(toUserName); textMessage.setCreateTime(new Date().getTime()); textMessage.setMsgType("text"); textMessage.setFuncFlag(0); // 文本消息 if (msgType.equals(MessageUtil.REQ_MESSAGE_TYPE_TEXT)) { if ("hi".equals(content)) { respMessage = "helloworld"; } } if (StringUtils.isNullOrEmpty(respMessage)) { textMessage.setContent(respContent); respMessage = MessageUtil.textMessageToXml(textMessage); } textMessage.setContent(respMessage); respMessage = MessageUtil.textMessageToXml(textMessage); } catch (Exception e) { e.printStackTrace(); } logger.info("respMessage is" + respMessage); return respMessage; }
BaseMessage.java:
package com.min.message; public class BaseMessage { // 接收方帐号(收到的OpenID) private String ToUserName; // 开发者微信号 private String FromUserName; // 消息创建时间 (整型) private long CreateTime; // 消息类型(text/music/news) private String MsgType; // 位0x0001被标志时,星标刚收到的消息 private int FuncFlag; 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 int getFuncFlag() { return FuncFlag; } public void setFuncFlag(int funcFlag) { FuncFlag = funcFlag; } }
TextMessage.java
package com.min.message; public class TextMessage extends BaseMessage { private String Content; public String getContent() { return Content; } public void setContent(String content) { Content = content; } }
效果图:
完工,以上只是最简单的接收文本消息,和回复文本消息。目前微信支持文本消息,图片消息,语音消息,视频消息,小视频消息,地理位置消息,链接消息。
支持回复文本消息,图片消息,语音消息,视频消息,音乐消息,图文消息。
以上です。
源代码下载 密码7774