在开始做之前,先简单介绍了微信公众平台的基本原理。
微信服务器就相当于一个转发服务器,终端(手机、Pad等)发起请求至微信服务器,微信服务器然后将请求转发给我们的应用服务器。应用服务器处理完毕后,将响应数据回发给微信服务器,微信服务器再将具体响应信息回复到微信App终端。
通信协议为:HTTP
数据传输格式为:XML
具体的流程如下图所示:
编写一个servlevt,在其中的doGet方法中定义校验方法,具体代码如下:
package me.gacl.wx.web.servlet;import javax.servlet.ServletException;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.security.MessageDigest;import java.security.NoSuchAlgorithmException;import java.util.Arrays;/** * Created by xdp on 2016/1/25. * 使用@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径 */@WebServlet(urlPatterns="/WxServlet")public class WxServlet extends HttpServlet { /** * Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性) * 比如这里我将Token设置为gacl */ private final String TOKEN = "gacl"; protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { } protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { System.out.println("开始校验签名"); /** * 接收微信服务器发送请求时传递过来的4个参数 */ String signature = request.getParameter("signature");//微信加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。 String timestamp = request.getParameter("timestamp");//时间戳 String nonce = request.getParameter("nonce");//随机数 String echostr = request.getParameter("echostr");//随机字符串 //排序 String sortString = sort(TOKEN, timestamp, nonce); //加密 String mySignature = sha1(sortString); //校验签名 if (mySignature != null && mySignature != "" && mySignature.equals(signature)) { System.out.println("签名校验通过。"); //如果检验成功输出echostr,微信服务器接收到此输出,才会确认检验完成。 //response.getWriter().println(echostr); response.getWriter().write(echostr); } else { System.out.println("签名校验失败."); } } /** * 排序方法 * * @param token * @param timestamp * @param nonce * @return */ public String sort(String token, String timestamp, String nonce) { String[] strArray = {token, timestamp, nonce}; Arrays.sort(strArray); StringBuilder sb = new StringBuilder(); for (String str : strArray) { sb.append(str); } return sb.toString(); } /** * 将字符串进行sha1加密 * * @param str 需要加密的字符串 * @return 加密后的内容 */ public String sha1(String str) { try { MessageDigest digest = MessageDigest.getInstance("SHA-1"); digest.update(str.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 ""; }}
我这里用的Servlet3.0,使用Servlet3.0的好处就是可以直接使用@WebServlet注解映射Servlet的访问路径,不再需要在web.xml文件中进行配置.
将WxStudy项目部署到Tomcat服务器中运行,直接启动项目,然后用ngrok将本地8080端口映射到外网(如何使用ngrok请参考博客《微信开发—微信开发环境搭建》)。如下图所示:
下面正式开始在工程中实现以上思路,因为返回的数据都是json格式,这里会用到阿里的fastjson库,为构造请求和处理请求后的数据序列化和反序列化提供支持。
1.定义一个AccessToken实体类
package me.gacl.wx.entry;/** * AccessToken的数据模型 * Created by xdp on 2016/1/25. */public class AccessToken { //获取到的凭证 private String accessToken; //凭证有效时间,单位:秒 private int expiresin; public String getAccessToken() { return accessToken; } public void setAccessToken(String accessToken) { this.accessToken = accessToken; } public int getExpiresin() { return expiresin; } public void setExpiresin(int expiresin) { this.expiresin = expiresin; }}
2.定义一个AccessTokenInfo类,用于存放获取到的AccessToken,代码如下:
package me.gacl.wx.Common;import me.gacl.wx.entry.AccessToken;/** * Created by xdp on 2016/1/25. */public class AccessTokenInfo { //注意是静态的 public static AccessToken accessToken = null;}
3.编写一个用于发起https请求的工具类NetWorkHelper,代码如下:
package me.gacl.wx.util;import javax.net.ssl.*;import java.io.BufferedReader;import java.io.InputStream;import java.io.InputStreamReader;import java.net.URL;import java.security.cert.CertificateException;import java.security.cert.X509Certificate;/** * 访问网络用到的工具类 */public class NetWorkHelper { /** * 发起Https请求 * @param reqUrl 请求的URL地址 * @param requestMethod * @return 响应后的字符串 */ public String getHttpsResponse(String reqUrl, String requestMethod) { URL url; InputStream is; String resultData = ""; try { url = new URL(reqUrl); HttpsURLConnection con = (HttpsURLConnection) url.openConnection(); TrustManager[] tm = {xtm}; SSLContext ctx = SSLContext.getInstance("TLS"); ctx.init(null, tm, null); con.setSSLSocketFactory(ctx.getSocketFactory()); con.setHostnameVerifier(new HostnameVerifier() { @Override public boolean verify(String arg0, SSLSession arg1) { return true; } }); con.setDoInput(true); //允许输入流,即允许下载 //在android中必须将此项设置为false con.setDoOutput(false); //允许输出流,即允许上传 con.setUseCaches(false); //不使用缓冲 if (null != requestMethod && !requestMethod.equals("")) { con.setRequestMethod(requestMethod); //使用指定的方式 } else { con.setRequestMethod("GET"); //使用get请求 } is = con.getInputStream(); //获取输入流,此时才真正建立链接 InputStreamReader isr = new InputStreamReader(is); BufferedReader bufferReader = new BufferedReader(isr); String inputLine; while ((inputLine = bufferReader.readLine()) != null) { resultData += inputLine + "n"; } System.out.println(resultData); } catch (Exception e) { e.printStackTrace(); } return resultData; } X509TrustManager xtm = new X509TrustManager() { @Override public X509Certificate[] getAcceptedIssuers() { return null; } @Override public void checkServerTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } @Override public void checkClientTrusted(X509Certificate[] arg0, String arg1) throws CertificateException { } };}
getHttpsResponse方法是请求一个https地址,参数requestMethod为字符串“GET”或者“POST”,传null或者“”默认为get方式。
4.定义一个默认启动的servlet,在init方法中启动一个新的线程去获取accessToken
package me.gacl.wx.web.servlet;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import me.gacl.wx.Common.AccessTokenInfo;import me.gacl.wx.entry.AccessToken;import me.gacl.wx.util.NetWorkHelper;import javax.servlet.ServletException;import javax.servlet.annotation.WebInitParam;import javax.servlet.annotation.WebServlet;import javax.servlet.http.HttpServlet;/** * 用于获取accessToken的Servlet * Created by xdp on 2016/1/25. */@WebServlet( name = "AccessTokenServlet", urlPatterns = {"/AccessTokenServlet"}, loadOnStartup = 1, initParams = { @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"), @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda") })public class AccessTokenServlet extends HttpServlet { @Override public void init() throws ServletException { System.out.println("启动WebServlet"); super.init(); final String appId = getInitParameter("appId"); final String appSecret = getInitParameter("appSecret"); //开启一个新的线程 new Thread(new Runnable() { @Override public void run() { while (true) { try { //获取accessToken AccessTokenInfo.accessToken = getAccessToken(appId, appSecret); //获取成功 if (AccessTokenInfo.accessToken != null) { //获取到access_token 休眠7000秒,大约2个小时左右 Thread.sleep(7000 * 1000); //Thread.sleep(10 * 1000);//10秒钟获取一次 } else { //获取失败 Thread.sleep(1000 * 3); //获取的access_token为空 休眠3秒 } } catch (Exception e) { System.out.println("发生异常:" + e.getMessage()); e.printStackTrace(); try { Thread.sleep(1000 * 10); //发生异常休眠1秒 } catch (Exception e1) { } } } } }).start(); } /** * 获取access_token * * @return AccessToken */ private AccessToken getAccessToken(String appId, String appSecret) { NetWorkHelper netHelper = new NetWorkHelper(); /** * 接口地址为 可以正常与微信服务器通信登录到我们的测试公众号的管理后台,然后用微信扫描一下测试号的二维码,如下图所示:
我们的公众号应用响应给微信用户的文本消息的XML数据如下:
1453755900000 测试公众号的管理后台也可以看到关注测试号的用户列表,如下图所示:
通过这个简单的入门程序,我们揭开了微信开发的神秘面纱了.
更多微信开发入门学习总结相关文章请关注PHP中文网!