微信公众号开发

微信公众号开发

微信公众号开发


request用于网络请求,它是对原生的http request的封装.

微信的数据包装方式是xml,所以我们要借助ejs这个模板库,把数据作为变量替换到xml字符汇中

还有一些工具模板:

lodash是一些常用的方法集,做数组拆分,类型判断等等.Heredoc是一个黑科技,把函数体里面的多行的注解作为字符串提取出来主要用来降低拼接字符串的成本

raw-body用来获取一个http请求返回的可读流的内容实体.

sha1进行加密.

微信服务器返回的数据是xml格式,无法用js函数直接使用,所以要xml2js这个模块把xml数据解析为js对象,方便我们使用.

微信公众号开发:配置接入流程,加密认证环节,票据access_token.

利用nodejs开发一些网页或者爬虫工具,来对nodejs api和它的技术特点有一些基本的认知.

有一些其他的后端经验,php,java,ruby,主要弄明白一个网络http请求从开发到结束中间所经过的环节.

nodejs和javascript

进击node.js教程

1

针对微信常用接口进行一个一个单独讲解和实现,并且有许多小案例演示.

开发电影公众号的网站项目.

了解一:本地代理环节的搭建以及最入门的加密认证.

1.域名服务器环境的配置
2.利用qq浏览器代理调式端口

重点:加密认证逻辑

微信公众号:企业号,订阅号,服务号

订阅号:个人,小团队,只要是信息的传播,管理用户以及用户的互动. 如消息定制

服务,企业和组织,提供业务服务与用户管理能力.比如支付,智能接口.

企业号,管理全学校所有学院等,各个部门上班人员的考勤,活动进程等.

服务号,管理全学校的水果商店或者打印店,可以直接支持送货上门,以及推送一些特价水果.

订阅号,管理一个班级,一个学院的信息订阅,通知和互动.

公众号

认证和非认证

认证:一般需要你有个开户过的企业大家可以法人身份去折腾下开一个小公司.

认证

订阅号:认证和非认证账号的区别就是,认证账号别可以直接在添加好友里搜索关键词就能找到你.

订阅号和服务号三点不同

出现位置不同.
单月发送消息的数量,订阅号一天一篇,服务号一个月最多4篇.
订阅号没有9大接口和支付功能.

服务号9大接口

1.语音识别
2.客服接口
3.OAuth 2.0网页的授权:这个授权接口,可以请求用户授权,从而拿到更多用户的信息.

4.生成带参数二维码:
公众号可以获取一系列携带不同参数的二维码,在用户扫描关注公众号后.
公众号可以根据参数分析各二维码的效果.这些参数可以自己定制,从而可以实现更多分析结果,比如用户从哪里来的.

5.获取用户的地理位置:公众号能够获得用户进入公众号会话时的地理位置.可以做微信导航.

6.获取用户基本信息:公众号可以根据加密后的用户OpenID,通过一系列的参数交互,最终拿到用户基础信息,包括头像,名称,性别,地区.

7.获取关注者的列表:通过这个接口,可以拿到所有关注者的OpenID,就知道有多少人关注你,是谁在关注你.
8.用户分组接口:通过分组接口,可以在后台为用户移动,创建,修改分组,比如把你们班级你们团队分成,男生一组,女生一组
9.上传下载多媒体文件

订阅号

1.会话界面的自定义菜单
2.多客服接口
3.获取用户地址位置
4.高级群发接口
5.用户分组接口

域名,服务器以及ngrok环境

微信<->服务器<->域名<->应用服务器<->微信

./ngork

www.tunnel.mobl

使用:
下载配置文件ngrok.cfg

1
ngrok -config ngrok.cfg - subdomain example 8080
1
2
3
4
5
6
7
cd
cd text
ls
// 写一个简单的服务器
python m SimpleHTTPServer 3100
python -m SimpleHTTPServer 3100
-config ngrok.cfg subdomain imooc-wechat 3100

子域名:

1
http://imooc-wechat.tunnel.mobi

使用nodejs的localtunnel服务

1
2
npm install -g localtunnel
lt --port 3100

PageKite 花生壳

配置,接入微信公众号

点击开发者工具:接口测试申请

获取:
url
token

接入公众号:

一:配置微信公众号后台
二:验证公众号

配置服务器的url

验证公众号

token,timestamp,nonce
字典排序,sha1加密
r===signature

一:将token,timestamp,nonce三个参数进行字典序排序
二:将三个参数字符串拼接成一个字符串进行sha1加密.
三:将加密后的字符串与signature对比,如果相同,表示这个请求来源于微信,我们直接原因返回echostr参数内容,接入验证就成功了.

实现加密认证逻辑

使用koa,不用express

使用Koa框架,代码可以更加精简,更加易懂,对于反反复复的异步交互更适合用这个框架实现.

1
2
3
scott/wechat >>

npm install koa sha1
1
2
3
4
5
6
7
8
9
node -v
node --harmony app.js

>> ./ngrok -config ngrok.cfg -subdomain scott_wechat 1234

ls --port 1234
// 把生成的地址配置到接口配置信息中url里面去

node --harmony app
1
https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1433401084

开发

  1. 填写服务器配置
  2. 验证服务器地址的有效性
  3. 根据接口文档实现业务逻辑

我们在手机上给微信公众号发送一个消息,这就相当于一个request请求,然后这个request到达微信服务器,紧接着微信服务器对这个request做出解析,然后开始响应一个response给你,这样你就收到了相应的回复。

当我们进行微信公众号的开发之后,用户的请求就不再有微信服务器去做处理了,而是由我们自己的服务做处理,然后把消息传给微信服务器,微信服务器再将消息返回给我们的用户。

在这个过程中,微信服务器起了一个中间商的作用,所以经过这样的分析,微信公众号开发要把握的一点就是,你只要返回给微信服务器正确的数据,微信服务器就能帮你做正确的事情。

所以作为微信公众号开发的第一步就是接入,你需要将自己的服务器和微信服务器做接通,只有接通之后,用户发送的请求到达微信服务器之后,微信服务器才知道该将这个请求转发给哪个服务器来处理。

问题一

这个url是用来接收微信服务器发送过来的请求的,要对请求做处理?想一想,request?处理?该怎么做呢?

1
2
3
4
5
6
7
8
9
10
11
12
13
public class OneServlet extends HttpServlet {

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override

protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}

}

需要

服务器
公众号

申请测试号
这里是申请地址:

1
https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login

appID 和appsecret

通过他们我们能得到很重要的一个数值就access_token
access_token是公众号的全局唯一接口调用凭据

公众号调用各接口时都需使用access_token

获取access_token

官方技术文档中已经说了,公众号调用接口都需要使用到access_token

1
2
// https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
1
2
3
grant_type	是	获取access_token填写client_credential
appid 是 第三方用户唯一凭证
secret 是 第三方用户唯一凭证密钥,即appsecret

问题二

这个接口正确调用会返回什么数据呢?

正常情况下会返回下述JSON数据包给公众号

1
{"access_token":"ACCESS_TOKEN","expires_in":7200}

那么这里又包含如下参数

1
2
access_token	获取到的凭证
expires_in 凭证有效时间,单位:秒

使用Postman这个工具简单演示一下发起get请求获取access_token 。

然后把响应的参数填写即可,这里的grant_type 就写默认的即可,剩下的appid和secret就填写你公众号的appID 和appsecret。

这个时候你就会发现,一个完整的get请求就拼接好了,接下来点击send即可,然后就会看到返回的数据

这样就得到我们需要的access_token 。

微信服务器会往我们填写的这个URL上面发送一个get请求,通过这个get请求能够得到微信服务器发送过来的一些消息,然后对消息做响应的处理来与微信服务器对接。

很显然,这个URL需要填写一个servlet,微信服务区器将消息发送过来之后需要对这个请求做处理,所以这里选择使用servlet对请求做处理。

问题三

关于如何验证,文档中给了一个PHP的例子,那么Java中该如何验证呢?

首先创建一个maven项目,然后创建一个CoreServlet用来接收微信服务器发送过来的请求

1
2
3
4
5
6
7
8
9
10
11
public class CoreServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}

@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

创建完成servlet之后就需要将这个servlet进行注册了,这个是在web.xml中

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app 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_3_0.xsd"
version="3.0">
<servlet>
<servlet-name>coreServlet</servlet-name>
<servlet-class>
com.ithuanqging.wechat.CoreServlet
</servlet-class>
</servlet>
<!-- url-pattern中配置的/coreServlet用于指定该Servlet的访问路径 -->
<servlet-mapping>
<servlet-name>coreServlet</servlet-name>
<url-pattern>/coreservlet</url-pattern>
</servlet-mapping>
</web-app>

填写服务器地址(URL)、Token和EncodingAESKey,其中URL是开发者用来接收微信消息和事件的接口URL。Token可由开发者可以任意填写,用作生成签名(该Token会和接口URL中包含的Token进行比对,从而验证安全性)。EncodingAESKey由开发者手动填写或随机生成,将用作消息体加解密密钥。

开发者可选择消息加解密方式:明文模式、兼容模式和安全模式。

第二步:验证消息的确来自微信服务器

1
2
3
4
signature	微信加密签名,signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
timestamp 时间戳
nonce 随机数
echostr 随机字符串

1)将token、timestamp、nonce三个参数进行字典序排序 2)将三个参数字符串拼接成一个字符串进行sha1加密 3)开发者获得加密后的字符串可与signature对比,标识该请求来源于微信

检验signature的PHP示例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private function checkSignature()
{
_GET["signature"];
_GET["timestamp"];
_GET["nonce"];

tmpArr = array(timestamp, $nonce);
sort($tmpArr, SORT_STRING);
$tmpStr = implode( $tmpArr );
$tmpStr = sha1( $tmpStr );

if( signature ){
return true;
}else{
return false;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 微信加密签名
String signature = req.getParameter("signature");
// 时间戳
String timestamp = req.getParameter("timestamp");
// 随机数
String nonce = req.getParameter("nonce");
// 随机字符串
String echostr = req.getParameter("echostr");

PrintWriter out = resp.getWriter();

// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}

##
微信测试公众号基本配置URL和Token的验证-JAVA

我们填写的URL和Token,微信那边会根据你填的来验证是否正确,URL必须有域名的,Token自己定义的。

微信调用我们服务器,会通过GET请求,请求带有signature、timestamp、nonce、echostr参数。服务端返回echostr参数,则表示token验证成功。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
       @GET
@Path("/tokenVarify")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@ApiOperation(value = "Vatify wc token", position = 1)
public void tokenVarify(@Context HttpServletRequest request,
@Context HttpServletResponse response) {
boolean isGet = request.getMethod().toLowerCase().equals("get");
PrintWriter print;
if (isGet) {
// 微信加密签名
String signature = request.getParameter("signature");
// 时间戳
String timestamp = request.getParameter("timestamp");
// 随机数
String nonce = request.getParameter("nonce");
// 随机字符串
String echostr = request.getParameter("echostr");
// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (signature != null && CheckoutUtil.checkSignature(signature, timestamp, nonce)) {
try {
print = response.getWriter();
print.write(echostr);
print.flush();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

CheckoutUtil.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

public class CheckoutUtil {
// 与接口配置信息中的Token要一致
private static String token = "bryant.zhang.test";

/**
* 验证签名
*
* @param signature
* @param timestamp
* @param nonce
* @return
*/
public static boolean checkSignature(String signature, String timestamp, String nonce) {
String[] arr = new String[] { token, timestamp, nonce };
// 将token、timestamp、nonce三个参数进行字典序排序
// Arrays.sort(arr);
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;
}

/**
* 将字节数组转换为十六进制字符串
*
* @param byteArray
* @return
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}

/**
* 将字节转换为十六进制字符串
*
* @param mByte
* @return
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}
public static void sort(String a[]) {
for (int i = 0; i < a.length - 1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[j].compareTo(a[i]) < 0) {
String temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
}
}
}

qi文档中说的真的很清楚了,包括如何验证等表述的都很清楚,下面我们直接编码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 微信加密签名
String signature = req.getParameter("signature");
// 时间戳
String timestamp = req.getParameter("timestamp");
// 随机数
String nonce = req.getParameter("nonce");
// 随机字符串
String echostr = req.getParameter("echostr");

PrintWriter out = resp.getWriter();

// 通过检验signature对请求进行校验,若校验成功则原样返回echostr,表示接入成功,否则接入失败
if (SignUtil.checkSignature(signature, timestamp, nonce)) {
out.print(echostr);
}
out.close();
out = null;
}

以上是从request请求中拿到传过来的参数,接下来就是需要创建一个工具类按照文档中说的方法去进行验证了,这里我已经写好了这个工具类SignUtil

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
public class SignUtil {
// 与接口配置信息中的Token要一致
private static String token = "mytoken";

/**
* 验证签名
*/
public static boolean checkSignature(String signature, String timestamp,String nonce) {
// 1.将token、timestamp、nonce三个参数进行字典序排序
String[] arr = new String[] { token, timestamp, nonce };
Arrays.sort(arr);

// 2. 将三个参数字符串拼接成一个字符串进行sha1加密
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;
// 3.将sha1加密后的字符串可与signature对比,标识该请求来源于微信
return tmpStr != null ? tmpStr.equals(signature.toUpperCase()) : false;
}

/**
* 将字节数组转换为十六进制字符串
*/
private static String byteToStr(byte[] byteArray) {
String strDigest = "";
for (int i = 0; i < byteArray.length; i++) {
strDigest += byteToHexStr(byteArray[i]);
}
return strDigest;
}

/**
* 将字节转换为十六进制字符串
*/
private static String byteToHexStr(byte mByte) {
char[] Digit = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A','B', 'C', 'D', 'E', 'F' };
char[] tempArr = new char[2];
tempArr[0] = Digit[(mByte >>> 4) & 0X0F];
tempArr[1] = Digit[mByte & 0X0F];
String s = new String(tempArr);
return s;
}}

以上这个工具类你可以直接拿来使用。

这个时候就要回到我们的测试公众号后台了,去填写我们的配置,也就是接口配置信息,但是现在你写的程序还在本地,微信即使发送请求也发不到你这啊,所以这个时候需要将的项目打包传到你的服务器上

首先将你的项目通过maven打包成war

1
G:\JavaCodeDemo\wechattest:mvn package
1
2
3
4
5
6
7
wechattest > target >
classes
generated-sources
maven-archiver
maven-status
wechattest
wechattest.war

接下来将这个war包上传到你的服务器上,我这里使用的是FileZilla这个工具

找到刚才生成的war包,远程站点必须是tomcat下的webapps目录下。

服务器配置的知识涉及到在服务器中安装JDK和tomcat!

现在只要告诉微信服务器你的这个项目中的CoreServlet的正确路径,就能接收微信服务器发动的消息了,现在回到测试公众号的后台。

接口配置信息:

1
2
服务器公网IP地址
coreservlet的路径
1
http://139.199.98.152/wechattest/coreservlet
-------------本文结束感谢您的阅读-------------
达叔小生 wechat
欢迎您扫一扫上面的微信公众号,订阅我的博客!
坚持原创技术分享,您的支持将鼓励我继续创作!
0%