【微信开发】微信开发 之 开启开发模式

作者 : 万境绝尘

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/20494177

GitHub源码位置 :

-- HTTP : https://github.com/han1202012/WeChatVerify.git

-- SSH : git@github.com:han1202012/WeChatVerify.git

CSDN下载认证程序的war包和源码 : http://download.csdn.net/detail/han1202012/6999207

微信验证war包下载 : 

-- 访问地址 : ip地址:80/WeChatVerify/verifyServlet ;

-- Token : 注意Token是 "hanshuliang" ;

一. 开启开发模式解析

1. 开发者文档

首先进入编辑模式, 将编辑模式关闭, 进入开发模式

点击查看文档, 就可以打开微信的开发者文档 : 路线 功能 -> 高级功能 -> 开发模式 -> 查看文档 ;

开发者文档目录结构说明

-- 新手接入 : 包括 接入指南 典型案例介绍 开发者规范, 讲解如何接入开发者模;

-- 基础支持 : 包括 获取access_token 全局返回码说明 接口频率限制说明 上传下载多媒体文件;

-- 接收消息 : 包括 验证消息真实性 接收普通消息 接收事件推送 接收语音识别结果;

-- 发送消息 : 包括 发送被动响应消息 发送客服消息;

-- 用户管理 : 包括 分组管理接口 获取用户基本信息 获取关注者列表 获取用户地理位置 网页授权用户基本信息 网页获取用户网络状态;

-- 自定义菜单 : 包括 自定义菜单创建接口 自定义菜单查询接口 自定义菜单删除接口 自定义菜单事件推送;

-- 推广支持 : 包括 生成带参数的二维码 ;

-- 微信 JS接口 : 包括 隐藏微信中网页右上角的按钮 隐藏微信中网页底部的导航栏 网页获取用户网络状态;

-- 开发者交流互助 : 包括 开发者问答系统 接口调试工具 接口体验测试号申请;

2. 开发者校验流程解析

(1)申请消息接口

点击开发模式 "成为开发者" 按钮之后, 会弹出协议

之后会弹出填写 URL 和 Token

-- URL : 用来接收微信服务器数据的接口URL;

-- Token : 任意填写, 用于生成签名;

(2) 验证URL有效性

校验流程 : 程序必须能够处理HTTP GET请求, 并对请求者身份进行校验, 确保请求来自微信服务器;

-- 获取参数 : HTTP GET 会携带四个参数 signature timestamp nonce echostr;

-- 拼装参数 : 将排序后的三个参数按照字典顺序排成字符串;

-- 加密参数 : 将排序后的字符串进行sha1加密;

-- 返回结果 : 将加密后的字符串 与 signature 参数对比, 如果相等则说明请求来自微信服务器, 原样返回参数 echostr;

二. 开发校验程序

1. 要点解析

(1) 在servlet中获取四个参数

获取方法 : 在doGet()方法中, 直接调用request的getParameter("signature")方法, 即可获取signature参数;

-- 示例 : 

代码语言:javascript
复制
		//获取微信服务器发送给我们的四个参数
		String signature = req.getParameter("signature");
		String timestamp = req.getParameter("timestamp");
		String nonce = req.getParameter("nonce");
		String echostr = req.getParameter("echostr");

(2) 将参数按照字典顺序排序

排序方法 : 数组工具类 Arrays.sort()会自动将数组中的字符串按照字典循序排序;

-- 代码示例

代码语言:javascript
复制
		//将token timestamp nonce 按照字典顺序排序
		String[] params = new String[]{token, timestamp, nonce};
		Arrays.sort(params);
	//将上面三个参数排序之后拼接成字符串
	StringBuffer buffer = new StringBuffer();
	for(int i = 0; i < params.length; i ++){
		buffer.append(params[i]);
	}</code></pre></div></div><h4 id="84bgu" name="(3)-sha1-%E5%8A%A0%E5%AF%86">(3) sha1 加密</h4><p><strong>获取MessageDigest加密类</strong> : 通过调用 MessageDigest.getInstance(&#34;SHA-1&#34;), 即可获取sha1加密类;</p><p>-- <strong>代码示例</strong> : </p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">			//获取sha1加密对象
		MessageDigest digest = MessageDigest.getInstance(&#34;SHA-1&#34;);
		//将组合后的字符串使用sha1加密, 加密后获得一个byte数组
		byte[] byteDigest = digest.digest(buffer.toString().getBytes());</code></pre></div></div><h4 id="94cdd" name="(4)-byte%E6%95%B0%E7%BB%84%E8%BD%AC%E4%B8%BA%E5%AD%97%E7%AC%A6%E4%B8%B2">(4) byte数组转为字符串</h4><p><strong>byte转为char类型</strong> : </p><p>-- <strong>示例代码</strong> : </p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">	/*
 * 将byte转为字符串
 */
public static String byte2HexStr(byte b) {
	char[] digit = {&#39;0&#39;, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;, &#39;5&#39;, &#39;6&#39;, &#39;7&#39;, &#39;8&#39;, &#39;9&#39;, &#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;, &#39;F&#39;};
	
	char[] temp = new char[2];
	temp[0] = digit[(b &gt;&gt;&gt; 4) &amp; 0x0F];
	temp[1] = digit[b &amp; 0x0F];
	
	String s = new String(temp);
	
	return s;
}</code></pre></div></div><p><strong>byte数组转为字符串</strong> : 需要调用上面的类;</p><p>-- <strong>示例代码</strong> : </p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">	/*
 * 将byte数组转为字符串
 */
public static String byte2str(byte[] byteArray) {
	String str = &#34;&#34;;
	for(int i = 0; i &lt; byteArray.length; i ++){
		str += byte2HexStr(byteArray[i]);
	}
	return str;
}</code></pre></div></div><h4 id="8nsd5" name="(5)-%E5%90%91%E5%BE%AE%E4%BF%A1%E6%9C%8D%E5%8A%A1%E5%99%A8%E5%8F%91%E9%80%81%E4%BF%A1%E6%81%AF">(5) 向微信服务器发送信息</h4><p><strong>发送信息 </strong>: 先获取输出流, 然后校验, 如果通过校验就向微信服务器发送信息;</p><p>-- <strong>示例代码</strong> : </p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">		//创建一个出处流, 用于向微信服务器发送数据
	PrintWriter out = resp.getWriter();
	
	//如果校验通过, 向微信服务器发送echostr参数
	if(VerifyUtils.checkSignature(signature, timestamp, nonce)){
		out.print(echostr);
	}
	
	//释放资源
	out.close();
	out = null;</code></pre></div></div><p>.</p><h3 id="f5mn9" name="2.-%E7%A8%8B%E5%BA%8F%E6%BA%90%E7%A0%81">2. 程序源码</h3><p><strong>GitHub源码位置</strong> :</p><p>-- <strong>HTTP</strong> : https://github.com/han1202012/WeChatVerify.git</p><p>-- <strong>SSH</strong> : git@github.com:han1202012/WeChatVerify.git</p><p><strong>CSDN下载认证程序的war包和源码</strong> : http://download.csdn.net/detail/han1202012/6999207</p><p><strong>微信验证war包下载</strong> : </p><p>-- <strong>访问地址</strong> : ip地址:80/WeChatVerify/verifyServlet ;</p><p>-- <strong>Token</strong> : 注意Token是 &#34;hanshuliang&#34; ;</p><p><strong>程序结构</strong> : </p><figure class=""><div class="rno-markdown-img-url" style="text-align:center"><div class="rno-markdown-img-url-inner" style="width:41.97%"><div style="width:100%"><img src="https://cdn.static.attains.cn/app/developer-bbs/upload/1722850848603000271.jpg" /></div></div></div></figure><p><strong>Servlet源码</strong> : </p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">package shuliang.han.vertify.servlet;

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;

import shuliang.han.vertify.VerifyUtils;

public class VerifyServlet extends HttpServlet {

private static final long serialVersionUID = 4440739483644L;

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	
	//获取微信服务器发送给我们的四个参数
	String signature = req.getParameter(&#34;signature&#34;);
	String timestamp = req.getParameter(&#34;timestamp&#34;);
	String nonce = req.getParameter(&#34;nonce&#34;);
	String echostr = req.getParameter(&#34;echostr&#34;);
	
	//创建一个出处流, 用于向微信服务器发送数据
	PrintWriter out = resp.getWriter();
	
	//如果校验通过, 向微信服务器发送echostr参数
	if(VerifyUtils.checkSignature(signature, timestamp, nonce)){
		out.print(echostr);
	}
	
	//释放资源
	out.close();
	out = null;
	
}

@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
		throws ServletException, IOException {
	//注意这里不能有任何操作, 否则不能完成验证
}

}

验证工具类源码

代码语言:javascript
复制
package shuliang.han.vertify;

import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Arrays;

public class VerifyUtils {

private final static String token = &#34;hanshuliang&#34;;

public static boolean checkSignature(String signature, String timestamp, String nonce) {
	
	//将token timestamp nonce 按照字典顺序排序
	String[] params = new String[]{token, timestamp, nonce};
	Arrays.sort(params);
	
	//将上面三个参数排序之后拼接成字符串
	StringBuffer buffer = new StringBuffer();
	for(int i = 0; i &lt; params.length; i ++){
		buffer.append(params[i]);
	}
	
	//str用于存储加密后的字符串
	String str = null;
	
	try {
		//获取sha1加密对象
		MessageDigest digest = MessageDigest.getInstance(&#34;SHA-1&#34;);
		//将组合后的字符串使用sha1加密, 加密后获得一个byte数组
		byte[] byteDigest = digest.digest(buffer.toString().getBytes());
		//获取加密后的字符串, 将byte数组转化为字符串
		str = byte2str(byteDigest);
	} catch (NoSuchAlgorithmException e) {
		e.printStackTrace();
	}
	
	/*
	 * 将加密后的字符串 与 signature 参数进行比较, 
	 * 如果加密后的字符串为null直接返回false
	 * 如果加密后的字符串不为null, 直接返回
	 */
	boolean isVerified = (str != null) ? str.equals(signature.toUpperCase()) : false;
	
	return isVerified;
}

/*
 * 将byte数组转为字符串
 */
public static String byte2str(byte[] byteArray) {
	String str = &#34;&#34;;
	for(int i = 0; i &lt; byteArray.length; i ++){
		str += byte2HexStr(byteArray[i]);
	}
	return str;
}

/*
 * 将byte转为字符串
 */
public static String byte2HexStr(byte b) {
	char[] digit = {&#39;0&#39;, &#39;1&#39;, &#39;2&#39;, &#39;3&#39;, &#39;4&#39;, &#39;5&#39;, &#39;6&#39;, &#39;7&#39;, &#39;8&#39;, &#39;9&#39;, &#39;A&#39;, &#39;B&#39;, &#39;C&#39;, &#39;D&#39;, &#39;E&#39;, &#39;F&#39;};
	
	char[] temp = new char[2];
	temp[0] = digit[(b &gt;&gt;&gt; 4) &amp; 0x0F];
	temp[1] = digit[b &amp; 0x0F];
	
	String s = new String(temp);
	
	return s;
}

}

web.xml配置文件

代码语言:javascript
复制
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">

&lt;servlet&gt;
	&lt;servlet-name&gt;verifyServlet&lt;/servlet-name&gt;
	&lt;servlet-class&gt;shuliang.han.vertify.servlet.VerifyServlet&lt;/servlet-class&gt;
&lt;/servlet&gt;

&lt;servlet-mapping&gt;
	&lt;servlet-name&gt;verifyServlet&lt;/servlet-name&gt;
	&lt;url-pattern&gt;/verifyServlet&lt;/url-pattern&gt;
&lt;/servlet-mapping&gt;

<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>

三. 进行校验

1.部署程序

(1) 导出war包

(2) 将war包上传到服务器

(3) 配置Tomcat的server.xml文件

微信服务器只能接受80端口数据

代码语言:javascript
复制
    <Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />

2. 使用阿**服务器

将上面的程序导出的war包, 放到阿里云Tomcat服务器下 : 使用 http://hanshuliang.com/WeChatVerify/verifyServlet 登陆;

-- 结果 : 这是正常情况, 因为在POST中没有添加参数;

HTTP Status 500 -


type Exception report

message

description The server encountered an internal error () that prevented it from fulfilling this request.

exception

代码语言:javascript
复制
java.lang.NullPointerException
java.lang.String.compareTo(String.java:1168)
java.lang.String.compareTo(String.java:92)
java.util.Arrays.mergeSort(Arrays.java:1144)
java.util.Arrays.sort(Arrays.java:1079)
shuliang.han.vertify.VerifyUtils.checkSignature(VerifyUtils.java:13)
shuliang.han.vertify.servlet.VerifyServlet.doGet(VerifyServlet.java:28)
javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)

note The full stack trace of the root cause is available in the Apache Tomcat/6.0.20 logs.


Apache Tomcat/6.0.20

不支持8080端口, 只支持80端口, 我的服务器上跑着ngix服务器, 独立域名博客在上面那 : 郁闷 ...

2. 开始校验

原来的Ngix服务器中80端口是独立域名博客, 现在讲独立域名博客指到 8086端口中 : 现在独立域名博客可以从 http://hanshuliang.com/ 访问;

将Tomcat设置成80端口, 气死我了 ;

终于成功了 : 哭死了, 调试了好长时间啊, 终于通过了;

接口功能不全, 只有基础接口的权限 ...

GitHub源码位置 :

-- HTTP : https://github.com/han1202012/WeChatVerify.git

-- SSH : git@github.com:han1202012/WeChatVerify.git

CSDN下载认证程序的war包和源码 : http://download.csdn.net/detail/han1202012/6999207

微信验证war包下载 : 

-- 访问地址 : ip地址:80/WeChatVerify/verifyServlet ;

-- Token : 注意Token是 "hanshuliang" ;

.

作者 : 万境绝尘

转载请注明出处 : http://blog.csdn.net/shulianghan/article/details/20494177