开发学习总结(⼆)——开发⼊门
上⼀篇《》我们已经完成了开发的准备⼯作,准备⼯作完成之后,就要开始步⼊正题了。
⼀、公众平台的基本原理
在开始做之前,先简单介绍了公众平台的基本原理。
服务器就相当于⼀个转发服务器,终端(⼿机、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⽅法中定义校验⽅法,具体代码如下:
1package me.gacl.wx.web.servlet;
2
3import javax.servlet.ServletException;
4import javax.servlet.annotation.WebServlet;
5import javax.servlet.http.HttpServlet;
6import javax.servlet.http.HttpServletRequest;
7import javax.servlet.http.HttpServletResponse;
8import java.io.IOException;
9import java.security.MessageDigest;
10import java.security.NoSuchAlgorithmException;
11import java.util.Arrays;
12
13/**
14 * Created by xdp on 2016/1/25.
15 * 使⽤@WebServlet注解配置WxServlet,urlPatterns属性指明了WxServlet的访问路径
16*/
17 @WebServlet(urlPatterns="/WxServlet")
18public class WxServlet extends HttpServlet {
19
20/**
21 * Token可由开发者可以任意填写,⽤作⽣成签名(该Token会和接⼝URL中包含的Token进⾏⽐对,从⽽验证安全性)
22 * ⽐如这⾥我将Token设置为gacl
23*/
24private final String TOKEN = "gacl";
25
26protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
27
28 }
29
30protected void doGet(HttpServletRequest request, HttpServletResponse response) throws Servlet
Exception, IOException {
31 System.out.println("开始校验签名");
32/**
33 * 接收服务器发送请求时传递过来的4个参数
34*/
35 String signature = Parameter("signature");//加密签名signature结合了开发者填写的token参数和请求中的timestamp参数、nonce参数。
36 String timestamp = Parameter("timestamp");//时间戳
37 String nonce = Parameter("nonce");//随机数
38 String echostr = Parameter("echostr");//随机字符串
39//排序
40 String sortString = sort(TOKEN, timestamp, nonce);
41//加密
42 String mySignature = sha1(sortString);
43//校验签名
44if (mySignature != null && mySignature != "" && mySignature.equals(signature)) {
45 System.out.println("签名校验通过。");
46//如果检验成功输出echostr,服务器接收到此输出,才会确认检验完成。
47//Writer().println(echostr);
48 Writer().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*/
63public String sort(String token, String timestamp, String nonce) {
64 String[] strArray = {token, timestamp, nonce};
65 Arrays.sort(strArray);
66 StringBuilder sb = new StringBuilder();
67for (String str : strArray) {
68 sb.append(str);
69 }
70
String();
72 }
73
74/**
75 * 将字符串进⾏sha1加密
76 *
77 * @param str 需要加密的字符串
78 * @return加密后的内容
79*/
80public String sha1(String str) {
81try {
82 MessageDigest digest = Instance("SHA-1");
83 digest.Bytes());
84byte messageDigest[] = digest.digest();
85// Create Hex String
86 StringBuffer hexString = new StringBuffer();
87// 字节数组转换为⼗六进制数
88for (int i = 0; i < messageDigest.length; i++) {
89 String shaHex = HexString(messageDigest[i] & 0xFF);
90if (shaHex.length() < 2) {
91 hexString.append(0);
92 }
93 hexString.append(shaHex);
94 }
String();
96
97 } catch (NoSuchAlgorithmException e) {
98 e.printStackTrace();
99 }
100return "";
101 }
102 }
我这⾥⽤的Servlet3.0,使⽤Servlet3.0的好处就是可以直接使⽤@WebServlet注解映射Servlet的访问路径,不再需要在l⽂件中进⾏配置.
将WxStudy项⽬部署到Tomcat服务器中运⾏,直接启动项⽬,然后⽤ngrok将本地8080端⼝映射到外⽹(如何使⽤ngrok请参考博客《》)。如下图所⽰:
测试是否可以通过k.natapp地址正常访问,测试结果如下:
可以看到,我们的项⽬已经可以被外⽹正常访问到了。
进⼊测试管理界⾯,在接⼝配置信息中填⼊映射的外⽹地址和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地址:api.weixin.qq/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
我们可以看到,调⽤过程中需要传递appID和AppSecret,appID和AppSecret是在申请的时候⾃动分配给的,相当于的⾝份标⽰,使⽤的注册帐号登录到腾讯提供的管理后台就可以看到⾃⼰申请的的AppID和AppSecret,如下图所⽰:
这是我申请测试帐号时分配到的AppID和AppSecret。
3.3、获取access_token⽅案以及具体实现
这⾥采⽤的⽅案是这样的,定义⼀个默认启动的servlet,在init⽅法中启动⼀个Thread,这个进程中定义⼀个⽆限循环的⽅法,⽤来获取access_token,当获取成功后,此进程休眠7000秒(7000秒=1.944444444444444⼩时),否则休眠3秒钟继续获取。流程图如下:
下⾯正式开始在⼯程中实现以上思路,因为返回的数据都是json格式,这⾥会⽤到阿⾥的fastjson库,为构造请求和处理请求后的数据序列化和反序列化提供⽀持。
1.定义⼀个AccessToken实体类
1package me.;
2
3/**
4 * AccessToken的数据模型
5 * Created by xdp on 2016/1/25.
6*/
7public class AccessToken {
8
9//获取到的凭证
10private String accessToken;
11//凭证有效时间,单位:秒
12private int expiresin;
13
14public String getAccessToken() {
15return accessToken;
16 }
17
18public void setAccessToken(String accessToken) {
19this.accessToken = accessToken;
20 }
21
22public int getExpiresin() {
23return expiresin;
24 }
25
26public void setExpiresin(int expiresin) {
28 }
29 }
2.定义⼀个AccessTokenInfo类,⽤于存放获取到的AccessToken,代码如下: 1package me.gacl.wx.Common;
2
3import me.AccessToken;
4
5/**
6 * Created by xdp on 2016/1/25.
7*/
8public class AccessTokenInfo {
9
10//注意是静态的
11public static AccessToken accessToken = null;
12 }
3.编写⼀个⽤于发起https请求的⼯具类NetWorkHelper,代码如下:
1package me.gacl.wx.util;
2
3import javax.ssl.*;
4import java.io.BufferedReader;
5import java.io.InputStream;
6import java.io.InputStreamReader;
7import java.URL;
8import CertificateException;
9import X509Certificate;
10
11/**
12 * 访问⽹络⽤到的⼯具类
13*/
14public class NetWorkHelper {
15
16/**
17 * 发起Https请求
18 * @param reqUrl 请求的URL地址
19 * @param requestMethod
20 * @return响应后的字符串
21*/
22public String getHttpsResponse(String reqUrl, String requestMethod) {
23 URL url;
24 InputStream is;
25 String resultData = "";
26try {
27 url = new URL(reqUrl);
28 HttpsURLConnection con = (HttpsURLConnection) url.openConnection();
29 TrustManager[] tm = {xtm};
30
31 SSLContext ctx = Instance("TLS");
32 ctx.init(null, tm, null);
33
34 con.SocketFactory());
35 con.setHostnameVerifier(new HostnameVerifier() {
36 @Override
37public boolean verify(String arg0, SSLSession arg1) {
38return true;
39 }
40 });
41
42
43 con.setDoInput(true); //允许输⼊流,即允许下载
44
45//在android中必须将此项设置为false
46 con.setDoOutput(false); //允许输出流,即允许上传
47 con.setUseCaches(false); //不使⽤缓冲
48if (null != requestMethod && !requestMethod.equals("")) {
49 con.setRequestMethod(requestMethod); //使⽤指定的⽅式
50 } else {
51 con.setRequestMethod("GET"); //使⽤get请求
52 }
53 is = InputStream(); //获取输⼊流,此时才真正建⽴链接
54 InputStreamReader isr = new InputStreamReader(is);
55 BufferedReader bufferReader = new BufferedReader(isr);
56 String inputLine;
57while ((inputLine = adLine()) != null) {
58 resultData += inputLine + "\n";
59 }
60 System.out.println(resultData);
61
62 } catch (Exception e) {
63 e.printStackTrace();
64 }
65return resultData;
66 }
67
68 X509TrustManager xtm = new X509TrustManager() {
69 @Override
70public X509Certificate[] getAcceptedIssuers() {
71return null;
72 }
73
74 @Override
75public void checkServerTrusted(X509Certificate[] arg0, String arg1)
76throws CertificateException {
77
78 }
79
80 @Override
81public void checkClientTrusted(X509Certificate[] arg0, String arg1)
82throws CertificateException {
83
84 }
85 };
86 }
getHttpsResponse⽅法是请求⼀个https地址,参数requestMethod为字符串“GET”或者“POST”,传null或者“”默认为get⽅式。
4.定义⼀个默认启动的servlet,在init⽅法中启动⼀个新的线程去获取accessToken
1package me.gacl.wx.web.servlet;
2
3import com.alibaba.fastjson.JSON;
4import com.alibaba.fastjson.JSONObject;
5import me.gacl.wx.Common.AccessTokenInfo;
6import me.AccessToken;
7import me.gacl.wx.util.NetWorkHelper;
8
9import javax.servlet.ServletException;
10import javax.servlet.annotation.WebInitParam;
11import javax.servlet.annotation.WebServlet;
12import javax.servlet.http.HttpServlet;
13
14/**
15 * ⽤于获取accessToken的Servlet
16 * Created by xdp on 2016/1/25.
17*/
18 @WebServlet(
19 name = "AccessTokenServlet",
20 urlPatterns = {"/AccessTokenServlet"},
21 loadOnStartup = 1,
22 initParams = {
23 @WebInitParam(name = "appId", value = "wxbe4d433e857e8bb1"),
24 @WebInitParam(name = "appSecret", value = "ccbc82d560876711027b3d43a6f2ebda")
25 })
26public class AccessTokenServlet extends HttpServlet {
27
28 @Override
29public void init() throws ServletException {
30 System.out.println("启动WebServlet");
31super.init();
32
33final String appId = getInitParameter("appId");
34final String appSecret = getInitParameter("appSecret");
35
36//开启⼀个新的线程
37new Thread(new Runnable() {
38 @Override
39public void run() {
40while (true) {
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论