(一)token的介绍
引用:access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需定时刷新,重复获取将导致上次获取的access_token失效!
(二)token的获取参考文档
(三)token获取流程分析
从公众平台获取账号的AppID和AppSecret;
token获取并解析存储执行体;
采用任务调度每隔两小时执行一次token获取执行体;
(四)token的获取流程的具体实现
①获取appid和appsecret
在微信公众平台接口测试工具中可以查看到我们需要的两个参数:
这里我们将appid 和secret 定义到配置文件【wechat.properties】,在src目录下新建【wechat.properties】文件,大致代码为:
#开发者的appid
appid=wx7e32765bc24XXXX
#开发者的AppSecret
AppSecret=d58051564fe9d86093f9XXXXX
②token获取并解析存储执行体的代码编写
由于在这里我们需要通过http的get请求向微信服务器获取时效性为7200秒的token,所以我在这里写了一个http请求的工具类HttpUtils,以方便我们的使用,如下:(这里需要导入文末的http协议包)
1 packagecom.gede.wechat.util;2 importjava.io.BufferedInputStream;3 importjava.io.BufferedReader;4 importjava.io.IOException;5 importjava.io.InputStream;6 importjava.io.InputStreamReader;7 importjava.io.OutputStreamWriter;8 importjava.net.MalformedURLException;9 importjava.net.URI;10 importjava.net.URL;11 importjava.net.URLConnection;12 importjava.util.ArrayList;13 importjava.util.List;14 importjava.util.Map;15 importjava.util.Set;16 importjava.util.zip.GZIPInputStream;17
18 importorg.apache.http.HttpResponse;19 importorg.apache.http.NameValuePair;20 importorg.apache.http.client.ClientProtocolException;21 importorg.apache.http.client.HttpClient;22 importorg.apache.http.client.entity.UrlEncodedFormEntity;23 importorg.apache.http.client.methods.HttpGet;24 importorg.apache.http.client.methods.HttpPost;25 importorg.apache.http.entity.StringEntity;26 importorg.apache.http.impl.client.DefaultHttpClient;27 importorg.apache.http.message.BasicNameValuePair;28 importorg.apache.http.protocol.HTTP;29 importorg.apache.http.util.EntityUtils;30 /**
31 *@authorgede32 *@versiondate:2019年5月26日 下午5:43:3633 * @description :34 */
35 public classHttpUtils {36
37 /**
38 * @Description: http get请求共用方法39 *@param @paramreqUrl40 *@param @paramparams41 *@param @return
42 *@param @throwsException43 */
44 @SuppressWarnings("resource")45 public static String sendGet(String reqUrl, Mapparams)46 throwsException {47 InputStream inputStream = null;48 HttpGet request = newHttpGet();49 try{50 String url =buildUrl(reqUrl, params);51 HttpClient client = newDefaultHttpClient();52
53 request.setHeader("Accept-Encoding", "gzip");54 request.setURI(newURI(url));55
56 HttpResponse response =client.execute(request);57
58 inputStream =response.getEntity().getContent();59 String result =getJsonStringFromGZIP(inputStream);60 returnresult;61 } finally{62 if (inputStream != null) {63 inputStream.close();64 }65 request.releaseConnection();66 }67
68 }69
70 @SuppressWarnings("resource")71 public static String sendPost(String reqUrl, Mapparams)72 throwsException {73 try{74 Set set =params.keySet();75 List list = new ArrayList();76 for(String key : set) {77 list.add(newBasicNameValuePair(key, params.get(key)));78 }79 if (list.size() > 0) {80 try{81 HttpClient client = newDefaultHttpClient();82 HttpPost request = newHttpPost(reqUrl);83
84 request.setHeader("Accept-Encoding", "gzip");85 request.setEntity(newUrlEncodedFormEntity(list, HTTP.UTF_8));86
87 HttpResponse response =client.execute(request);88
89 InputStream inputStream =response.getEntity().getContent();90 try{91 String result =getJsonStringFromGZIP(inputStream);92
93 returnresult;94 } finally{95 inputStream.close();96 }97 } catch(Exception ex) {98 ex.printStackTrace();99 throw new Exception("网络连接失败,请连接网络后再试");100 }101 } else{102 throw new Exception("参数不全,请稍后重试");103 }104 } catch(Exception ex) {105 ex.printStackTrace();106 throw new Exception("发送未知异常");107 }108 }109
110 public staticString sendPostBuffer(String urls, String params)111 throwsClientProtocolException, IOException {112 HttpPost request = newHttpPost(urls);113
114 StringEntity se = newStringEntity(params, HTTP.UTF_8);115 request.setEntity(se);116 //发送请求
117 @SuppressWarnings("resource")118 HttpResponse httpResponse = newDefaultHttpClient().execute(request);119 //得到应答的字符串,这也是一个 JSON 格式保存的数据
120 String retSrc =EntityUtils.toString(httpResponse.getEntity());121 request.releaseConnection();122 returnretSrc;123
124 }125
126 public staticString sendXmlPost(String urlStr, String xmlInfo) {127 //xmlInfo xml具体字符串
128
129 try{130 URL url = newURL(urlStr);131 URLConnection con =url.openConnection();132 con.setDoOutput(true);133 con.setRequestProperty("Pragma:", "no-cache");134 con.setRequestProperty("Cache-Control", "no-cache");135 con.setRequestProperty("Content-Type", "text/xml");136 OutputStreamWriter out = newOutputStreamWriter(137 con.getOutputStream());138 out.write(new String(xmlInfo.getBytes("utf-8")));139 out.flush();140 out.close();141 BufferedReader br = new BufferedReader(newInputStreamReader(142 con.getInputStream()));143 String lines = "";144 for (String line = br.readLine(); line != null; line =br145 .readLine()) {146 lines = lines +line;147 }148 return lines; //返回请求结果
149 } catch(MalformedURLException e) {150 e.printStackTrace();151 } catch(IOException e) {152 e.printStackTrace();153 }154 return "fail";155 }156
157 private staticString getJsonStringFromGZIP(InputStream is) {158 String jsonString = null;159 try{160 BufferedInputStream bis = newBufferedInputStream(is);161 bis.mark(2);162 //取前两个字节
163 byte[] header = new byte[2];164 int result =bis.read(header);165 //reset输入流到开始位置
166 bis.reset();167 //判断是否是GZIP格式
168 int headerData =getShort(header);169 //Gzip 流 的前两个字节是 0x1f8b
170 if (result != -1 && headerData == 0x1f8b) {171 //LogUtil.i("HttpTask", " use GZIPInputStream ");
172 is = newGZIPInputStream(bis);173 } else{174 //LogUtil.d("HttpTask", " not use GZIPInputStream");
175 is =bis;176 }177 InputStreamReader reader = new InputStreamReader(is, "utf-8");178 char[] data = new char[100];179 intreadSize;180 StringBuffer sb = newStringBuffer();181 while ((readSize = reader.read(data)) > 0) {182 sb.append(data, 0, readSize);183 }184 jsonString =sb.toString();185 bis.close();186 reader.close();187 } catch(Exception e) {188 e.printStackTrace();189 }190
191 returnjsonString;192 }193
194 private static int getShort(byte[] data) {195 return (data[0] << 8) | data[1] & 0xFF;196 }197
198 /**
199 * 构建get方式的url200 *201 *@paramreqUrl202 * 基础的url地址203 *@paramparams204 * 查询参数205 *@return构建好的url206 */
207 public static String buildUrl(String reqUrl, Mapparams) {208 StringBuilder query = newStringBuilder();209 Set set =params.keySet();210 for(String key : set) {211 query.append(String.format("%s=%s&", key, params.get(key)));212 }213 return reqUrl + "?" +query.toString();214 }215
216 }
我们在做http请求的时候需要目标服务器的url,这里在项目中为了方便对url的管理我们src目录下建立了interface_url.properties用于存放目标url,这里我们将请求token的url存入:
#获取token的url
tokenUrl=https://api.weixin.qq.com/cgi-bin/token
我们需要将我们配置的配置文件在项目初始化后能得到启动,所以我在这里加入一个项目初始化的代码InterfaceUrlIntiServlet来实现,用于项目启动初始化interface_url.properties和wechat.properties中的配置:
1 packagecom.gede.web.start;2 importjavax.servlet.ServletConfig;3 importjavax.servlet.ServletException;4 importjavax.servlet.http.HttpServlet;5 /**
6 *@authorgede7 *@versiondate:2019年5月26日 下午7:42:148 * @description :9 */
10 public class InterfaceUrlIntiServlet extendsHttpServlet {11
12 private static final long serialVersionUID = 1L;13
14 @Override15 public void init(ServletConfig config) throwsServletException {16 InterfaceUrlInti.init();17 }18 }
初始化的具体实现,将初始化过后的方法都存入到GlobalConstants中方便项目中随意调用,如下:
1 packagecom.gede.web.start;2 importjava.io.IOException;3 importjava.io.InputStream;4 importjava.util.Properties;5 importcom.gede.web.util.GlobalConstants;6 /**
7 *@authorgede8 *@versiondate:2019年5月26日 下午7:42:379 * @description :10 */
11 public classInterfaceUrlInti {12
13 public synchronized static voidinit(){14 ClassLoader cl =Thread.currentThread().getContextClassLoader();15 Properties props = newProperties();16 if(GlobalConstants.interfaceUrlProperties==null){17 GlobalConstants.interfaceUrlProperties = newProperties();18 }19 InputStream in = null;20 try{21 in = cl.getResourceAsStream("interface_url.properties");22 props.load(in);23 for(Object key : props.keySet()){24 GlobalConstants.interfaceUrlProperties.put(key, props.get(key));25 }26
27 props = newProperties();28 in = cl.getResourceAsStream("wechat.properties");29 props.load(in);30 for(Object key : props.keySet()){31 GlobalConstants.interfaceUrlProperties.put(key, props.get(key));32 }33
34 } catch(IOException e) {35 e.printStackTrace();36 }finally{37 if(in!=null){38 try{39 in.close();40 } catch(IOException e) {41 e.printStackTrace();42 }43 }44 }45 return;46 }47
48 }
这里用到的GlobalConstants,我们新建在web.util包里面,代码如下:
packagecom.gede.web.util;importjava.util.Properties;/***@authorgede
*@versiondate:2019年5月26日 下午7:45:27
* @description :*/
public classGlobalConstants {public staticProperties interfaceUrlProperties;/*** @Description: TODO
*@param @paramkey
*@param @return
*/
public staticString getInterfaceUrl(String key) {return(String) interfaceUrlProperties.get(key);
}
}
当我们把所有的准备工作都做好了之后我们可以开始真正的去获取token了,这里我们将获取到的token解析之后依然存储到GlobalConstants中方便使用,简单代码如下:(这里需要导入我们附件中的json包)
1 packagecom.gede.wechat.common;2 importjava.text.SimpleDateFormat;3 importjava.util.Date;4 importjava.util.HashMap;5 importjava.util.Map;6
7 importcom.gede.web.util.GlobalConstants;8 importcom.gede.wechat.util.HttpUtils;9
10 importnet.sf.json.JSONObject;11 /**
12 *@authorgede13 *@versiondate:2019年5月26日 下午7:50:3814 * @description :15 */
16 public classWeChatTask {17 /**
18 * @Description: 任务执行体19 *@param @throwsException20 */
21 public void getToken_getTicket() throwsException {22 Map params = new HashMap();23 params.put("grant_type", "client_credential");24 params.put("appid", GlobalConstants.getInterfaceUrl("appid"));25 params.put("secret", GlobalConstants.getInterfaceUrl("AppSecret"));26 String jstoken =HttpUtils.sendGet(27 GlobalConstants.getInterfaceUrl("tokenUrl"), params);28 String access_token =JSONObject.fromObject(jstoken).getString(29 "access_token"); //获取到token并赋值保存
30 GlobalConstants.interfaceUrlProperties.put("access_token", access_token);31 System.out.println(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date())+"token为=============================="+access_token);32 }33
34 }
(三)采用任务调度每隔两小时执行一次token获取执行体
我们阅读过微信的文档会发现我们的token获取的接口每天是有调用次数限制的,为了防止我们业务量比较大的情况下token的直接调用的接口次数不够用,所以我们需要根据token的时效性(7200s)在自己的业务服务器上做到token的缓存并定时获取,我这里用到的任务调度的方式是采用quartz,下面具体代码的实现:
1 packagecom.gede.wechat.quartz;2 importorg.apache.log4j.Logger;3 importcom.gede.wechat.common.WeChatTask;4
5 /**
6 *@authorgede7 *@versiondate:2019年5月26日 下午8:00:438 * @description :9 */
10 public classQuartzJob{11 private static Logger logger = Logger.getLogger(QuartzJob.class);12 /**
13 * @Description: 任务执行获取token14 *@param
15 */
16 public voidworkForToken() {17 try{18 WeChatTask timer = newWeChatTask();19 timer.getToken_getTicket();20 } catch(Exception e) {21 logger.error(e, e);22 }23 }24 }
这里新建配置文件spring-quartz.xml以方便quartz任务的管理和启用,这里将我们需要用到的workForToken()加入到执行任务中:
1 <?xml version="1.0" encoding="UTF-8"?>
2
3
4
5
6
7
8
9 class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
10
11
12
13
14
15
16 workForToken
17
18
19
20
21
22
23
24
25
26 0 0/1 * * * ?
27
28
29
30
31 class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
32
33
34
35
36
37
38
这里我为了测试将执行间隔时间设置成了1分钟一次,根据需要可以自行修改执行时间。
好了到这里我们就已经大功告成,就差初始化加载InterfaceUrlIntiServlet和开启quartz的使用:修改web.xml ,加入相关语句,代码如下:
1 <?xml version="1.0" encoding="UTF-8"?>
2
3 mychat
4
5 org.springframework.web.context.ContextLoaderListener
6
7
8 contextConfigLocation
9 classpath:applicationContext.xml,classpath:spring-quartz.xml
10
11
12
13 appServlet
14 org.springframework.web.servlet.DispatcherServlet
15
16 contextConfigLocation
17
18 classpath:appServlet.xml19
20
21 1
22
23
24
25 log4jConfigLocation
26 classpath:log4j.properties
27
28
29 org.springframework.web.util.Log4jConfigListener
30
31
32
33 interface_url-init_servlet
34 com.gede.web.start.InterfaceUrlIntiServlet
35 1
36
37
38
39 appServlet
40 /
41
42
当这一切都准备完毕之后我们启动项目,会发现每间隔一分钟就会有token获取到,这里我是将其存储在项目变量中,这里看一下我们的效果图和项目总的目录结构:
附件:今天需要导入的包有很多,最后我就给大家打一个压缩包,大家在运行之前将包全部导入即可。点击下载