总想自己动动手系列·3·微信公众号和外网服务交互之通过TOKEN验证(准备篇·1)
一、准备工作
(1)准备一个微信公众号(对私的订阅号或者对公的服务号)。
(2)准备一台部署了web应用,并且已经发布出去的Linux服务器(需要说明的是:微信公众号强烈建议使用80端口,使用其他自定义端口貌似根本不通,后面会有说明)。
先科普说明一下:
1.微信公众号的注册分为2种类型:对个人的订阅号,对企业的服务号,这个很简单,按照官方注册流程按部就班地填写基本不会出现问题。
服务号:主要偏向于服务交互(功能类似12315,114,银行,提供绑定信息,服务交互),每月可群发4条消息;服务号适用人群:**媒体、企业、政府或其他组织。
订阅号:主要偏向于为用户传达资讯,(功能类似报纸杂志,为用户提供新闻信息或娱乐趣事),每天可群发1条消息;订阅号适用人群**:个人、媒体、企业、政府或其他组织。
2.刚注册的订阅号和公众号是“非认证”状态的(本人注册的是对私的订阅号,目前处于非认证状态)非认证的订阅号对接口的访问权限是很小的,连起码的自定义订阅号的菜单都不给,真气人!
3.微信官方将认证审核流程托管到了第三方机构或公司,这个是要收取服务费的,对公的服务号收费标准:300¥/次/年。具体详细,网上是这样回答的:
我等会就去试一下如何认证,如果认证成功了,会出一篇具体介绍如何认证对私的订阅号的。本文注重介绍如何验证TOKEN的。
4.用户操作访问微信公众号,请求是如何处理转发的呢?看完下面的示意图就会明白为什么要准备一台自己的服务器了。
二、TOKEN验证的流程:
先假设,我们部署在服务器上的web应用已经可以正常被外网访问了。
(1)在微信公众号后台:开发-》基本设置菜单中
你以为就这么简单填写就把微信服务平台和自己的服务器web应用关联起来了???那是不可能滴!!!这样直接提交会报错
先简单介绍下图中的几个输入项和选择项到底是干嘛的!
1、URL:这个是TOKEN的验证调取的请求url,端口要求是80或者443,我们一般用80端口。
2、Token:这个是微信服务和自己的服务之间通信的关键通信凭证(也可以这么理解:开发者自定的验证口令)。
3、EncodingAESKey:你也可以理解为通信信息加密因子,这个让他它自动生成即可。
4、消息加解密方式:明文模式:不加解密(新手开发者建议选这个,方便开发和调试)。
(2)在自己服务器web项目中需要做调整,调整流程如下:
1、新建一个servlet.java用于处理微信服务器发送过来的Token验证请求:
package com.xfwl.weixinToken; import java.io.IOException; import java.io.PrintWriter; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class WXServletToken extends HttpServlet { /** * 构造器 */ public WXServletToken() { super(); } /** * 销毁操作 */ public void destroy() { super.destroy(); } /** * doGet请求处理 */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { /** * 获取weixin请求参数 */ String signature=request.getParameter("signature"); String timestamp=request.getParameter("timestamp"); String nonce=request.getParameter("nonce"); String echostr=request.getParameter("echostr"); /** * 校验参数是否正确 */ PrintWriter out = response.getWriter(); if(CheckUtil.checkSignature(signature, timestamp, nonce)){ //如果校验成功,将得到的随机字符串原路返回 out.print(echostr); } out.flush(); out.close(); } /** * doPost请求操作 */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType("text/html"); PrintWriter out = response.getWriter(); out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">"); out.flush(); out.close(); } /** *初始化操作 */ public void init() throws ServletException {} }
2、新建一个TOKEN验证类
1 package com.xfwl.weixinToken; 2 3 import java.security.MessageDigest; 4 import java.security.NoSuchAlgorithmException; 5 import java.util.Arrays; 6 7 /** 8 * 微信Token校验 9 * @author Jason 10 * 11 */ 12 public class CheckUtil { 13 /** 14 * 加密规则 15 */ 16 public static final String wx_mdType="SHA1"; 17 /** 18 * 微信公众号Token信息 19 */ 20 public static final String wx_token="*****"; 21 /** 22 * 校验微信Token的有效性 23 * @param signature 24 * @param timestamp 时间戳 25 * @param nonce 26 * @return 27 */ 28 public static boolean checkSignature(String signature,String timestamp,String nonce){ 29 //1、定义数组存放wx_token、timestamp、nonce 30 String[] arr={wx_token,timestamp,nonce}; 31 //2、对数组进行排序 32 Arrays.sort(arr); 33 //3、生成字符串 34 StringBuffer sb=new StringBuffer(); 35 for(String s:arr){ 36 sb.append(s); 37 } 38 String temp=getSha1(sb.toString()); 39 if(temp==null){ 40 return false; 41 } 42 return temp.equals(signature); 43 } 44 public static String getSha1(String data){ 45 if(data==null || data.length()==0){ 46 return null; 47 } 48 char[] hexDigist={'0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'}; 49 try { 50 MessageDigest mdTemp=MessageDigest.getInstance(wx_mdType); 51 mdTemp.update(data.getBytes()); 52 byte[] md=mdTemp.digest(); 53 int j=md.length; 54 char[] buf=new char[j*2]; 55 int k=0; 56 for(int i=0;i<j;i++){ 57 byte byte0=md[i]; 58 buf[k++]=hexDigist[byte0 >>> 4 & 0xf]; 59 buf[k++]=hexDigist[byte0 & 0xf]; 60 } 61 return new String(buf); 62 63 } catch (NoSuchAlgorithmException e) { 64 System.out.println("微信公众号TOKEN加密失败"); 65 return null; 66 } 67 } 68 }
3、web.xml中配置请求(当然了,用注解形式也可,方式很多种,任选随意)
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app version="3.0" 3 xmlns="http://java.sun.com/xml/ns/javaee" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xsi:schemaLocation="http://java.sun.com/xml/ns/javaee 6 http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"> 7 <display-name></display-name> 8 <!-- 默认界面 --> 9 <welcome-file-list> 10 <welcome-file>index.jsp</welcome-file> 11 </welcome-file-list> 12 <!-- 配置过滤器 --> 13 <!-- <filter> 14 <filter-name>filter</filter-name> 15 <filter-class>com.xfwl.filter.RequestFilter</filter-class> 16 <init-param> 17 <param-name>charset</param-name> 18 <param-value>UTF-8</param-value> 19 </init-param> 20 <init-param> 21 <param-name>contentType</param-name> 22 <param-value>text/html;charset=UTF-8</param-value> 23 </init-param> 24 </filter> 25 <filter-mapping> 26 <filter-name>filter</filter-name> 27 * 代表截获所有的请求 或指定请求/test.do /xxx.do 28 <url-pattern>/*</url-pattern> 29 </filter-mapping> --> 30 <!-- 接入微信Token验证 --> 31 <servlet> 32 <servlet-name>WXServletToken</servlet-name> 33 <servlet-class>com.xfwl.weixinToken.WXServletToken</servlet-class> 34 </servlet> 35 36 <servlet-mapping> 37 <servlet-name>WXServletToken</servlet-name> 38 <url-pattern>/servlet/WXServletToken</url-pattern> 39 </servlet-mapping> 40 </web-app>
4、打war包,重新部署启动
5、回到微信公众号后台:开发-》基本设置菜单中,输入对应的信息,点击提交按钮。即可提交设置成功。
需要说明一点:图中“IP白名单设置”,这个需要把自己服务器外网IP加上,如果你还需要使用微信公众号的接口调试功能去测试接口的话,那么微信公众号平台的IP(39.130.165.18)也需要加入白名单中。
6、到这一步:就已经完成了TOKEN的验证了。