上一篇《微信开发学习总结(一)——微信开发环境搭建》我们已经完成了微信开发的准备工作,准备工作完成之后,就要开始步入正题了。
一、微信公众平台的基本原理
在开始做之前,先简单介绍了微信公众平台的基本原理。
微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。
通信协议为:HTTP
数据传输格式为:XML
具体的流程如下图所示:
来一张更加直观的图吧:
我们需要做的事情,就是对微信服务器转发的HTTP请求做出响应。具体的请求内容,我们按照特定的XML格式去解析,处理完毕后,也要按照特定的XML格式返回。
二、微信公众号接入
在微信公众平台开发者文档上,关于公众号接入这一节内容在接入指南上写的比较详细的,文档中说接入公众号需要3个步骤,分别是:
1、填写服务器配置
2、验证服务器地址的有效性
3、依据接口文档实现业务逻辑
其实,第3步已经不能算做公众号接入的步骤,而是接入之后,开发人员可以根据微信公众号提供的接口所能做的一些开发。
第1步中服务器配置包含服务器地址(URL)、Token和EncodingAESKey。
服务器地址即公众号后台提供业务逻辑的入口地址,目前只支持80端口,之后包括接入验证以及任何其它的操作的请求(例如消息的发送、菜单管理、素材管理等)都要从这个地址进入。接入验证和其它请求的区别就是,接入验证时是get请求,其它时候是post请求;
Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性);
EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。本例中全部以未加密的明文消息方式,不涉及此配置项。
第2步,验证服务器地址的有效性,当点击“提交”按钮后,微信服务器将发送一个http的get请求到刚刚填写的服务器地址,并且携带四个参数:
接到请求后,我们需要做如下三步,若确认此次GET请求来自微信服务器,原样返回echostr参数内容,则接入生效,否则接入失败。
1. 将token、timestamp、nonce三个参数进行字典序排序
2. 将三个参数字符串拼接成一个字符串进行sha1加密
3. 开发者获得加密后的字符串可与signature对比,标识该请求来源于微信
下面我们用Java代码来演示一下这个验证过程
使用IDE(Eclipse或者IntelliJ IDEA)创建一个JavaWeb项目,这里我使用的是IntelliJ IDEA,项目目录结构如下图所示:
编写一个servlevt,在其中的doGet方法中定义校验方法,具体代码如下:
1 package me.gacl.wx.web.servlet; 2 3 import javax.servlet.ServletException; 4 import javax.servlet.annotation.WebServlet; 5 import javax.servlet.http.HttpServlet; 6 import javax.servlet.http.HttpServletRequest; 7 import javax.servlet.http.HttpServletResponse; 8 import java.io.IOException; 9 import java.security.MessageDigest; 10 import java.security.NoSuchAlgorithmException; 11 import java.util.Arrays; 12 13 /** 14 * Created by xdp on 2016/1/25. 15 * 使用@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径 16 */ 17 @WebServlet(urlPatterns="/WxServlet") 18 public class WxServlet extends HttpServlet { 19 20 /** 21 * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性) 22 * 比如这里我将Token设置为gacl 23 */ 24 private final String TOKEN = "gacl"; 25 26 protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 27 28 } 29 30 protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { 31 System.out.println("开始校验签名"); 32 /** 33 * 接收微信服务器发送请求时传递过来的4个参数 34 */ 35 String signature = request.getParameter("signature");//微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 36 String timestamp = request.getParameter("timestamp");//时间戳 37 String nonce = request.getParameter("nonce");//随机数 38 String echostr = request.getParameter("echostr");//随机字符串 39 //排序 40 String sortString = sort(TOKEN, timestamp, nonce); 41 //加密 42 String mySignature = sha1(sortString); 43 //校验签名 44 if (mySignature != null && mySignature != "" && mySignature.equals(signature)) { 45 System.out.println("签名校验通过。"); 46 //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。 47 //response.getWriter().println(echostr); 48 response.getWriter().write(echostr); 49 } else { 50 System.out.println("签名校验失败."); 51 } 52 53 } 54 55 /** 56 * 排序方法 57 * 58 * @param token 59 * @param timestamp 60 * @param nonce 61 * @return 62 */ 63 public String sort(String token, String timestamp, String nonce) { 64 String[] strArray = {token, timestamp, nonce}; 65 Arrays.sort(strArray); 66 StringBuilder sb = new StringBuilder(); 67 for (String str : strArray) { 68 sb.append(str); 69 } 70 71 return sb.toString(); 72 } 73 74 /** 75 * 将字符串进行sha1加密 76 * 77 * @param str 需要加密的字符串 78 * @return 加密后的内容 79 */ 80 public String sha1(String str) { 81 try { 82 MessageDigest digest = MessageDigest.getInstance("SHA-1"); 83 digest.update(str.getBytes()); 84 byte messageDigest[] = digest.digest(); 85 // Create Hex String 86 StringBuffer hexString = new StringBuffer(); 87 // 字节数组转换为 十六进制 数 88 for (int i = 0; i < messageDigest.length; i++) { 89 String shaHex = Integer.toHexString(messageDigest[i] & 0xFF); 90 if (shaHex.length() < 2) { 91 hexString.append(0); 92 } 93 hexString.append(shaHex); 94 } 95 return hexString.toString(); 96 97 } catch (NoSuchAlgorithmException e) { 98 e.printStackTrace(); 99 } 100 return ""; 101 } 102 }
我这里用的Servlet3.0,使用Servlet3.0的好处就是可以直接使用@WebServlet注解映射Servlet的访问路径,不再需要在web.xml文件中进行配置.
将WxStudy项目部署到Tomcat服务器中运行,直接启动项目,然后用ngrok将本地8080端口映射到外网(如何使用ngrok请参考博客《微信开发学习总结(一)——微信开发环境搭建》)。如下图所示:
测试是否可以通过http://xdp.ngrok.natapp.cn地址正常访问,测试结果如下:
可以看到,我们的项目已经可以被外网正常访问到了。
进入微信测试公众号管理界面,在接口配置信息中填入映射的外网地址和token,如下图所示:
点击提交按钮,页面会提示配置成功,
IDE的控制台中输出了校验通过的信息,如下图所示:
到此,我们的公众号应用已经能够和微信服务器正常通信了,也就是说我们的公众号已经接入到微信公众平台了。
三、access_token管理
3.1、access_token介绍
我们的公众号和微信服务器对接成功之后,接下来要做的就是根据我们的业务需求调用微信公众号提供的接口来实现相应的逻辑了。在使用微信公众号接口中都需要一个access_token。
关于access_token,在微信公众平台开发者文档上的获取接口调用凭据有比较详细的介绍:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,开发者需要妥善保存access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效。并且每天调用获取access_token接口的上限是2000次。
总结以上说明,access_token需要做到以下两点:
1.因为access_token有2个小时的时效性,要有一个机制保证最长2个小时重新获取一次。
2.因为接口调用上限每天2000次,所以不能调用太频繁。
3.2、微信公众平台提供的获取access_token的接口
关于access_token的获取方式,在微信公众平台开发者文档上有说明,公众号可以调用一个叫"获取access token"的接口来获取access_token。
获取access token接口调用请求说明
http请求方式: GET
请求的URL地址:https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET