Java微信公眾平台開發(六)--微信開發中的token獲取


轉自:http://www.cuiyongzhi.com/post/44.html

(一)token的介紹

引用:access_token是公眾號的全局唯一票據,公眾號調用各接口時都需使用access_token。開發者需要進行妥善保存。access_token的存儲至少要保留512個字符空間。access_token的有效期目前為2個小時,需定時刷新,重復獲取將導致上次獲取的access_token失效!

(二)token的獲取參考文檔

獲取的流程我們完全可以參考微信官方文檔:http://mp.weixin.qq.com/wiki/14/9f9c82c1af308e3b14ba9b973f99a8ba.html 如圖:

1.png

(三)token獲取流程分析

  • 從公眾平台獲取賬號的AppID和AppSecret;

  • token獲取並解析存儲執行體;

  • 采用任務調度每隔兩小時執行一次token獲取執行體;

(四)token的獲取流程的具體實現

①獲取appid和appsecret

在微信公眾平台【開發】——>【基本配置】中可以查看到我們需要的兩個參數:

2.png

這里我們將他們定義到我們的配置文件【wechat.properties】中,大致代碼為:

1
2
3
4
#獲取到的appid
appid=wx7e32765bc24XXXX 
#獲取到的AppSecret
AppSecret=d58051564fe9d86093f9XXXXX

②token獲取並解析存儲執行體的代碼編寫

由於在這里我們需要通過http的get請求向微信服務器獲取時效性為7200秒的token,所以我在這里寫了一個http請求的工具類,以方便我們的使用,如下:

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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package  com.cuiyongzhi.wechat.util;
 
import  java.io.BufferedInputStream;
import  java.io.BufferedReader;
import  java.io.IOException;
import  java.io.InputStream;
import  java.io.InputStreamReader;
import  java.io.OutputStreamWriter;
import  java.net.MalformedURLException;
import  java.net.URI;
import  java.net.URL;
import  java.net.URLConnection;
import  java.util.ArrayList;
import  java.util.List;
import  java.util.Map;
import  java.util.Set;
import  java.util.zip.GZIPInputStream;
 
import  org.apache.http.HttpResponse;
import  org.apache.http.NameValuePair;
import  org.apache.http.client.ClientProtocolException;
import  org.apache.http.client.HttpClient;
import  org.apache.http.client.entity.UrlEncodedFormEntity;
import  org.apache.http.client.methods.HttpGet;
import  org.apache.http.client.methods.HttpPost;
import  org.apache.http.entity.StringEntity;
import  org.apache.http.impl.client.DefaultHttpClient;
import  org.apache.http.message.BasicNameValuePair;
import  org.apache.http.protocol.HTTP;
import  org.apache.http.util.EntityUtils;
 
/**
  * ClassName: HttpUtils
 
  * @Description: http請求工具類
  * @author dapengniao
  * @date 2016年3月10日 下午3:57:14
  */
@SuppressWarnings ( "deprecation" )
public  class  HttpUtils {
 
     /**
      * @Description: http get請求共用方法
      * @param @param reqUrl
      * @param @param params
      * @param @return
      * @param @throws Exception
      * @author dapengniao
      * @date 2016年3月10日 下午3:57:39
      */
     @SuppressWarnings ( "resource" )
     public  static  String sendGet(String reqUrl, Map<String, String> params)
             throws  Exception {
         InputStream inputStream =  null ;
         HttpGet request =  new  HttpGet();
         try  {
             String url = buildUrl(reqUrl, params);
             HttpClient client =  new  DefaultHttpClient();
 
             request.setHeader( "Accept-Encoding" "gzip" );
             request.setURI( new  URI(url));
 
             HttpResponse response = client.execute(request);
 
             inputStream = response.getEntity().getContent();
             String result = getJsonStringFromGZIP(inputStream);
             return  result;
         finally  {
             if  (inputStream !=  null ) {
                 inputStream.close();
             }
             request.releaseConnection();
         }
 
     }
 
     /**
      * @Description: http post請求共用方法
      * @param @param reqUrl
      * @param @param params
      * @param @return
      * @param @throws Exception
      * @author dapengniao
      * @date 2016年3月10日 下午3:57:53
      */
     @SuppressWarnings ( "resource" )
     public  static  String sendPost(String reqUrl, Map<String, String> params)
             throws  Exception {
         try  {
             Set<String> set = params.keySet();
             List<NameValuePair> list =  new  ArrayList<NameValuePair>();
             for  (String key : set) {
                 list.add( new  BasicNameValuePair(key, params.get(key)));
             }
             if  (list.size() >  0 ) {
                 try  {
                     HttpClient client =  new  DefaultHttpClient();
                     HttpPost request =  new  HttpPost(reqUrl);
 
                     request.setHeader( "Accept-Encoding" "gzip" );
                     request.setEntity( new  UrlEncodedFormEntity(list, HTTP.UTF_8));
 
                     HttpResponse response = client.execute(request);
 
                     InputStream inputStream = response.getEntity().getContent();
                     try  {
                         String result = getJsonStringFromGZIP(inputStream);
 
                         return  result;
                     finally  {
                         inputStream.close();
                     }
                 catch  (Exception ex) {
                     ex.printStackTrace();
                     throw  new  Exception( "網絡連接失敗,請連接網絡后再試" );
                 }
             else  {
                 throw  new  Exception( "參數不全,請稍后重試" );
             }
         catch  (Exception ex) {
             ex.printStackTrace();
             throw  new  Exception( "發送未知異常" );
         }
     }
 
     /**
      * @Description: http post請求json數據
      * @param @param urls
      * @param @param params
      * @param @return
      * @param @throws ClientProtocolException
      * @param @throws IOException
      * @author dapengniao
      * @date 2016年3月10日 下午3:58:15
      */
     public  static  String sendPostBuffer(String urls, String params)
             throws  ClientProtocolException, IOException {
         HttpPost request =  new  HttpPost(urls);
 
         StringEntity se =  new  StringEntity(params, HTTP.UTF_8);
         request.setEntity(se);
         // 發送請求
         @SuppressWarnings ( "resource" )
         HttpResponse httpResponse =  new  DefaultHttpClient().execute(request);
         // 得到應答的字符串,這也是一個 JSON 格式保存的數據
         String retSrc = EntityUtils.toString(httpResponse.getEntity());
         request.releaseConnection();
         return  retSrc;
 
     }
 
     /**
      * @Description: http請求發送xml內容
      * @param @param urlStr
      * @param @param xmlInfo
      * @param @return
      * @author dapengniao
      * @date 2016年3月10日 下午3:58:32
      */
     public  static  String sendXmlPost(String urlStr, String xmlInfo) {
         // xmlInfo xml具體字符串
 
         try  {
             URL url =  new  URL(urlStr);
             URLConnection con = url.openConnection();
             con.setDoOutput( true );
             con.setRequestProperty( "Pragma:" "no-cache" );
             con.setRequestProperty( "Cache-Control" "no-cache" );
             con.setRequestProperty( "Content-Type" "text/xml" );
             OutputStreamWriter out =  new  OutputStreamWriter(
                     con.getOutputStream());
             out.write( new  String(xmlInfo.getBytes( "utf-8" )));
             out.flush();
             out.close();
             BufferedReader br =  new  BufferedReader( new  InputStreamReader(
                     con.getInputStream()));
             String lines =  "" ;
             for  (String line = br.readLine(); line !=  null ; line = br
                     .readLine()) {
                 lines = lines + line;
             }
             return  lines;  // 返回請求結果
         catch  (MalformedURLException e) {
             e.printStackTrace();
         catch  (IOException e) {
             e.printStackTrace();
         }
         return  "fail" ;
     }
 
     private  static  String getJsonStringFromGZIP(InputStream is) {
         String jsonString =  null ;
         try  {
             BufferedInputStream bis =  new  BufferedInputStream(is);
             bis.mark( 2 );
             // 取前兩個字節
             byte [] header =  new  byte [ 2 ];
             int  result = bis.read(header);
             // reset輸入流到開始位置
             bis.reset();
             // 判斷是否是GZIP格式
             int  headerData = getShort(header);
             // Gzip 流 的前兩個字節是 0x1f8b
             if  (result != - 1  && headerData ==  0x1f8b ) {
                 // LogUtil.i("HttpTask", " use GZIPInputStream  ");
                 is =  new  GZIPInputStream(bis);
             else  {
                 // LogUtil.d("HttpTask", " not use GZIPInputStream");
                 is = bis;
             }
             InputStreamReader reader =  new  InputStreamReader(is,  "utf-8" );
             char [] data =  new  char [ 100 ];
             int  readSize;
             StringBuffer sb =  new  StringBuffer();
             while  ((readSize = reader.read(data)) >  0 ) {
                 sb.append(data,  0 , readSize);
             }
             jsonString = sb.toString();
             bis.close();
             reader.close();
         catch  (Exception e) {
             e.printStackTrace();
         }
 
         return  jsonString;
     }
 
     private  static  int  getShort( byte [] data) {
         return  (data[ 0 ] <<  8 ) | data[ 1 ] &  0xFF ;
     }
 
     /**
      * 構建get方式的url
     
      * @param reqUrl
      *            基礎的url地址
      * @param params
      *            查詢參數
      * @return 構建好的url
      */
     public  static  String buildUrl(String reqUrl, Map<String, String> params) {
         StringBuilder query =  new  StringBuilder();
         Set<String> set = params.keySet();
         for  (String key : set) {
             query.append(String.format( "%s=%s&" , key, params.get(key)));
         }
         return  reqUrl +  "?"  + query.toString();
     }
 
}

我們在做http請求的時候需要目標服務器的url,這里在項目中為了方便對url的管理我們在資源目錄下建立了interface_url.properties用於存放目標url,這里我們將請求token的url存入:

1
2
#獲取token的url

我們需要將我們配置的配置文件在項目初始化后能得到啟動,所以我在這里加入一個項目初始化的代碼實現,用於項目啟動初始化interface_url.properties和wechat.properties中的配置:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package  com.cuiyongzhi.web.start;
 
import  javax.servlet.ServletConfig;
import  javax.servlet.ServletException;
import  javax.servlet.http.HttpServlet;
 
/**
  * ClassName: InterfaceUrlIntiServlet
  * @Description: 項目啟動初始化servlet
  * @author dapengniao
  * @date 2016年3月10日 下午4:08:43
  */
public  class  InterfaceUrlIntiServlet  extends  HttpServlet {
 
     private  static  final  long  serialVersionUID = 1L;
 
     @Override
     public  void  init(ServletConfig config)  throws  ServletException {
         InterfaceUrlInti.init();
     }
 
}

初始化的具體實現,將初始化過后的方法都存入到GlobalConstants中方便項目中隨意調用,如下:

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
package  com.cuiyongzhi.web.start;
 
import  java.io.IOException;
import  java.io.InputStream;
import  java.util.Properties;
 
import  com.cuiyongzhi.web.util.GlobalConstants;
 
/**
  * ClassName: InterfaceUrlInti
  * @Description: 項目啟動初始化方法
  * @author dapengniao
  * @date 2016年3月10日 下午4:08:21
  */
public  class  InterfaceUrlInti {
 
     public  synchronized  static  void  init(){
         ClassLoader cl = Thread.currentThread().getContextClassLoader();
         Properties props =  new  Properties();
         if (GlobalConstants.interfaceUrlProperties== null ){
             GlobalConstants.interfaceUrlProperties =  new  Properties();
         }
         InputStream in =  null ;
         try  {
             in = cl.getResourceAsStream( "interface_url.properties" );
             props.load(in);
             for (Object key : props.keySet()){
                 GlobalConstants.interfaceUrlProperties.put(key, props.get(key));
             }
             
             props =  new  Properties();
             in = cl.getResourceAsStream( "wechat.properties" );
             props.load(in);
             for (Object key : props.keySet()){
                 GlobalConstants.interfaceUrlProperties.put(key, props.get(key));
             }
             
         catch  (IOException e) {
             e.printStackTrace();
         } finally {
             if (in!= null ){
                 try  {
                     in.close();
                 catch  (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
         return ;
     }
 
}

當我們把所有的准備工作都做好了之后我們可以開始真正的去獲取token了,這里我們將獲取到的token解析之后依然存儲到GlobalConstants中方便使用,簡單代碼如下:

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
package  com.cuiyongzhi.wechat.common;
 
import  java.util.HashMap;
import  java.util.Map;
 
import  net.sf.json.JSONObject;
 
import  com.cuiyongzhi.web.util.GlobalConstants;
import  com.cuiyongzhi.wechat.util.HttpUtils;
 
/**
  * ClassName: WeChatTask
  * @Description: 微信兩小時定時任務體
  * @author dapengniao
  * @date 2016年3月10日 下午1:42:29
  */
public  class  WeChatTask {
     /**
      * @Description: 任務執行體
      * @param @throws Exception
      * @author dapengniao
      * @date 2016年3月10日 下午2:04:37
      */
     public  void  getToken_getTicket()  throws  Exception {
         Map<String, String> params =  new  HashMap<String, String>();
         params.put( "grant_type" "client_credential" );
         params.put( "appid" , GlobalConstants.getInterfaceUrl( "appid" ));
         params.put( "secret" , GlobalConstants.getInterfaceUrl( "AppSecret" ));
         String jstoken = HttpUtils.sendGet(
                 GlobalConstants.getInterfaceUrl( "tokenUrl" ), params);
         String access_token = JSONObject.fromObject(jstoken).getString(
                 "access_token" );  // 獲取到token並賦值保存
         GlobalConstants.interfaceUrlProperties.put( "access_token" , access_token);
                 System.out.println( new  SimpleDateFormat( "yyyy-MM-dd HH:mm:ss" ).format( new  Date())+ "token為==============================" +access_token);
     }
 
}

(三)采用任務調度每隔兩小時執行一次token獲取執行體

我們閱讀過微信的文檔會發現我們的token獲取的接口每天是有調用次數限制的,為了防止我們業務量比較大的情況下token的直接調用的接口次數不夠用,所以我們需要根據token的時效性(7200s)在自己的業務服務器上做到token的緩存並定時獲取,我這里用到的任務調度的方式是采用quartz,有關quartz的使用可以參考文章 http://cuiyongzhi.com/?tags=%E5%AE%9A%E6%97%B6%E4%BB%BB%E5%8A%A1 ,下面具體代碼的實現:

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
package  com.cuiyongzhi.wechat.quartz;
 
import  org.apache.log4j.Logger;
 
import  com.cuiyongzhi.wechat.common.WeChatTask;
 
public  class  QuartzJob{
     private  static  Logger logger = Logger.getLogger(QuartzJob. class );
     /**
      * @Description: 任務執行獲取token
      * @param    
      * @author dapengniao
      * @date 2016年3月10日 下午4:34:26
      */
     public  void  workForToken() {
         try  {
             WeChatTask timer =  new  WeChatTask();
             timer.getToken_getTicket();
         catch  (Exception e) {
             logger.error(e, e);
         }
     }
 
 
}

這里新建配置文件spring-quartz.xml以方便quartz任務的管理和啟用,這里將我們需要用到的workForToken()加入到執行任務中:

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
<? xml  version = "1.0"  encoding = "UTF-8" ?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
 
< beans >
     <!-- 要調用的工作類 -->
     < bean  id = "quartzJob"  class = "com.cuiyongzhi.wechat.quartz.QuartzJob" ></ bean >
     
     <!-- 定義調用對象和調用對象的方法 -->
     < bean  id = "jobtaskForToken"
         class = "org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean" >
         <!-- 調用的類 -->
         < property  name = "targetObject" >
             < ref  bean = "quartzJob"  />
         </ property >
         <!-- 調用類中的方法 -->
         < property  name = "targetMethod" >
             < value >workForToken</ value >
         </ property >
 
     </ bean >
     <!-- 定義觸發時間 -->
     < bean  id = "doTimeForToken"  class = "org.springframework.scheduling.quartz.CronTriggerBean" >
         < property  name = "jobDetail" >
             < ref  bean = "jobtaskForToken"  />
         </ property >
         <!-- cron表達式 -->
         < property  name = "cronExpression" >
             < value >0 0/1 * * * ?</ value >
         </ property >
     </ bean >
 
 
     <!-- 總管理類 如果將lazy-init='false'那么容器啟動就會執行調度程序 -->
     < bean  id = "startQuertz"  lazy-init = "false"  autowire = "no"
         class = "org.springframework.scheduling.quartz.SchedulerFactoryBean" >
         < property  name = "triggers" >
             < list >
                 < ref  bean = "doTimeForToken"  />
             </ list >
         </ property >
     </ bean >
 
</ beans >

這里我為了測試將執行間隔時間設置成了1分鍾一次,根據需要可以自行修改執行時間;最后我們需要在我們的web.xml啟動項中開啟quartz的使用:

1
2
3
4
5
< context-param >
         < param-name >contextConfigLocation</ param-name >
         < param-value >classpath:spring.xml,classpath:spring-mybatis.xml,classpath:spring-quartz.xml</ param-value >
         <!-- ,classpath:spring-quartz.xml 用於做任務調度 任務定時都可以 -->
     </ context-param >

當這一切都准備完畢之后我們啟動項目,會發現每間隔一分鍾就會有token獲取到,這里我是將其存儲在項目變量中,但是如果需要考慮到項目橫向擴展這里建議將token存儲到緩存中;運行結果如下:

3.png

那么到這里token的獲取和保存就基本講完了,下一篇將講述【多媒體消息的回復】,感謝你的翻閱,如果有需要源碼或有疑問可以留言!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM