微信公衆平臺開發——如何保證access_token長期有效?

摘要:由CSDN和《程序員》雜誌聯合主辦的2014年微信開發者大會將於8月23日在北京舉行。CSDN優秀博主、暢銷書做者柳峯受邀擔任講師,帶來《智能客服與LBS開發的「坑」與解決之道》的主題演講,歡迎來現場與柳峯深度溝通。

【編者按】由CSDN和《程序員》雜誌聯合主辦的 2014年微信開發者大會將於8月23日在北京舉行。內容涵蓋企業服務號開發和高級應用、企業號開發、業務系統對接、高級接口運用、微信支付、智能客服與LBS、HTML5社交應用、微信電商、微信廣告自助平臺等多方面。做爲一線微信開發商雲集、專一在開發實踐方面的頂級技術活動,到目前爲止本次活動已經獲得了業界的極大關注度。爲此,主辦方還總結了技術團隊 不該錯過2014年微信開發者大會的八個理由。>>> 立刻搶票參會java

值得一提的是,CSDN優秀博主、暢銷書《微信公衆平臺應用開發:方法、技巧與案例》的做者柳峯受邀作本次活動講師之一,演講主題《智能客服與LBS開發的「坑」與解決之道》。他在CSDN博客中(查看)撰寫了幾十篇+的微信開發技術文章,多篇訪問量都在3萬+,如下這篇《 微信公衆平臺開發教程第22篇-如何保證access_token長期有效》摘自他的博客。程序員

全文以下:web

爲了使第三方開發者可以爲用戶提供更多更有價值的個性化服務,微信公衆平臺開放了許多接口,包括自定義菜單接口、客服接口、獲取用戶信息接口、用戶分組接口、羣發接口等,開發者在調用這些接口時,都須要傳入一個相同的參數access_token,它是公衆帳號的全局惟一票據,它是接口訪問憑證。數據庫

access_token的有效期是7200秒(兩小時),在有效期內,能夠一直使用,只有當access_token過時時,才須要再次調用接口獲取access_token。在理想狀況下,一個7x24小時運行的系統,天天只須要獲取12次access_token,即每2小時獲取一次。若是在有效期內,再次獲取access_token,那麼上一次獲取的access_token將失效。api

目前,獲取access_token接口的調用頻率限制爲2000次/天,若是每次發送客服消息、獲取用戶信息、羣發消息以前都要先調用獲取access_token接口獲得接口訪問憑證,這顯然是不合理的,一方面會更耗時(多了一次接口調用操做),另外一方面2000次/天的調用限制恐怕也不夠用。所以,在實際應用中,咱們須要將獲取到的access_token存儲起來,而後按期調用access_token接口更新它,以保證隨時取出的access_token都是有效的。服務器

下面將爲你們介紹如何定時獲取並存儲access_token。請注意:這不是一篇講解如何調用接口獲取access_token的文章,關於access_token的獲取,請參考文章 《微信公衆賬號開發教程第14篇-自定義菜單的建立及菜單事件響應》微信

在動手前先來簡單分析一下,咱們要解決的無非是以下兩個問題:微信開發

1.如何定時獲取access_token?app

在Java中,若是要定時執行某項任務,須要用到java.util.Timer類,對於喜歡使用框架的朋友,能夠採用 開源的任務調度框架quartz,Spring框架也支持quartz。除此這外,還有一種方法就是啓動一個線程,在線程的run()方法中寫一個死循環,而後使用Thread.sleep()來保證線程定時執行某項任務。微信公衆平臺

2.將access_token保存在哪?

對於access_token的存儲,能夠考慮存儲在文件、數據庫或內存中。具體採用哪一種存儲方式,須要根據項目實際狀況而定。若是隻有一臺服務器,直接將access_token存儲在內存中是最簡便有效的方式。

在本文中,筆者將演示的按期獲取並存儲access_token的流程爲:Web服務器啓動時就加載一個Servlet,在Servlet的init()方法中啓動一個線程,在線程的run()方法中經過死循環+Thread.sleep()的方式按期獲取access_token,而後將獲取到的access_token保存在public static修飾的變量中。

在工程中建立一個InitServlet類,該類的代碼以下:

[js] view plaincopy

  1. package org.liufeng.weixin.servlet;    

  2.     

  3. import javax.servlet.ServletException;    

  4. import javax.servlet.http.HttpServlet;    

  5. import org.liufeng.weixin.thread.TokenThread;    

  6. import org.liufeng.weixin.util.WeixinUtil;    

  7. import org.slf4j.Logger;    

  8. import org.slf4j.LoggerFactory;    

  9.     

  10. /**  

  11.  * 初始化servlet  

  12.  *   

  13.  * @author liuyq  

  14.  * @date 2013-05-02  

  15.  */    

  16. public class InitServlet extends HttpServlet {    

  17.     private static final long serialVersionUID = 1L;    

  18.     private static Logger log = LoggerFactory.getLogger(WeixinUtil.class);    

  19.     

  20.     public void init() throws ServletException {    

  21.         // 獲取web.xml中配置的參數    

  22.         TokenThread.appid = getInitParameter("appid");    

  23.         TokenThread.appsecret = getInitParameter("appsecret");    

  24.     

  25.         log.info("weixin api appid:{}", TokenThread.appid);    

  26.         log.info("weixin api appsecret:{}", TokenThread.appsecret);    

  27.     

  28.         // 未配置appid、appsecret時給出提示    

  29.         if ("".equals(TokenThread.appid) || "".equals(TokenThread.appsecret)) {    

  30.             log.error("appid and appsecret configuration error, please check carefully.");    

  31.         } else {    

  32.             // 啓動定時獲取access_token的線程    

  33.             new Thread(new TokenThread()).start();    

  34.         }    

  35.     }    

  36. }    

從上面的代碼能夠看出,InitServlet類只重寫了init()方法,並無重寫doGet()和doPost()兩個方法,由於咱們並不打算讓InitServlet來處理訪問請求。init()方法的實現也比較簡單,先獲取在web.xml中配置的參數appid和appsecret,再啓動線程TokenThread定時獲取access_token。

InitServlet在web.xml中的配置以下:

[js] view plaincopy

  1. <?xml version="1.0" encoding="UTF-8"?>    

  2. <web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee"    

  3.     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"    

  4.     xsi:schemaLocation="http://java.sun.com/xml/ns/javaee     

  5.     <a href="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd" "="">http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"</a>>    

  6.     

  7.     <servlet>    

  8.         <servlet-name>initServlet</servlet-name>    

  9.         <servlet-class>    

  10.             org.liufeng.weixin.servlet.InitServlet    

  11.         </servlet-class>    

  12.         <!-- 配置獲取access_token所需參數appid和appsecret -->    

  13.         <init-param>    

  14.             <param-name>appid</param-name>    

  15.             <param-value>wx617a123bb8bc99cd</param-value>    

  16.         </init-param>    

  17.         <init-param>    

  18.             <param-name>appsecret</param-name>    

  19.             <param-value>4d82cbbbb08714c12345b62d7hn3dcb8</param-value>    

  20.         </init-param>    

  21.         <load-on-startup>0</load-on-startup>    

  22.     </servlet>    

  23.     

  24.     <welcome-file-list>    

  25.         <welcome-file>index.jsp</welcome-file>    

  26.     </welcome-file-list>    

  27. </web-app>    

InitServlet在web.xml中的配置與普通Servlet的配置有幾點區別:1)經過配置<init-param>向Servlet中傳入參數;2)經過配置<load-on-startup>使得Web服務器啓動時就加載該Servlet;3)沒有配置<servlet-mapping>,由於InitServlet並不對外提供訪問。

TokenThread的源代碼以下:

[js] view plaincopy

  1. package org.liufeng.weixin.thread;    

  2.     

  3. import org.liufeng.weixin.pojo.AccessToken;    

  4. import org.liufeng.weixin.util.WeixinUtil;    

  5. import org.slf4j.Logger;    

  6. import org.slf4j.LoggerFactory;    

  7.     

  8. /**  

  9.  * 定時獲取微信access_token的線程  

  10.  *   

  11.  * @author liuyq  

  12.  * @date 2013-05-02  

  13.  */    

  14. public class TokenThread implements Runnable {    

  15.     private static Logger log = LoggerFactory.getLogger(TokenThread.class);    

  16.     // 第三方用戶惟一憑證    

  17.     public static String appid = "";    

  18.     // 第三方用戶惟一憑證密鑰    

  19.     public static String appsecret = "";    

  20.     public static AccessToken accessToken = null;    

  21.     

  22.     public void run() {    

  23.         while (true) {    

  24.             try {    

  25.                 accessToken = WeixinUtil.getAccessToken(appid, appsecret);    

  26.                 if (null != accessToken) {    

  27.                     log.info("獲取access_token成功,有效時長{}秒 token:{}", accessToken.getExpiresIn(), accessToken.getToken());    

  28.                     // 休眠7000秒    

  29.                     Thread.sleep((accessToken.getExpiresIn() - 200) * 1000);    

  30.                 } else {    

  31.                     // 若是access_token爲null,60秒後再獲取    

  32.                     Thread.sleep(60 * 1000);    

  33.                 }    

  34.             } catch (InterruptedException e) {    

  35.                 try {    

  36.                     Thread.sleep(60 * 1000);    

  37.                 } catch (InterruptedException e1) {    

  38.                     log.error("{}", e1);    

  39.                 }    

  40.                 log.error("{}", e);    

  41.             }    

  42.         }    

  43.     }    

  44. }  

代碼中的第23行經過while(true){}構造了一個死循環(永久執行);第25行調用公衆平臺接口獲取access_token;第29行讓線程休眠7000秒再運行,即每隔7000秒獲取一次access_token,保證access_token永不失效。在項目中的其餘類,能夠經過調用 TokenThread.accessToken.getToken() 來獲得接口訪問憑證access_token。在本地部署運行該程序,Tomcat啓動完成後就會在控制檯顯示以下日誌:

[js] view plaincopy

  1. [INFO ] weixin api appid:wx617a123bb8bc99cd    

  2. [INFO ] weixin api appsecret:4d82cbbbb08714c12345b62d7hn3dcb8    

  3. [INFO ] 獲取access_token成功,有效時長7200秒 token:sFopJ9lMmLl4u-ad61ojKpS0TolhN2s3SnHoI2Mh5GgdiYb35i-7DG2T2CDyQKMe 

爲了可以直觀看到按期獲取access_token的效果,能夠試着將TokenThread裏的線程休眠時間修改成30秒或60秒。最後,附上本文所涉及的項目源代碼,下載地址:http://download.csdn.net/detail/lyq8479/7300501

PS:2014年4月25日微信團隊發佈了修改access_token長度的通知,不少開發者問此次修改會對咱們的程序產生什麼影響,這裏順便回答一下:若是開發者將獲取到的access_token存入數據庫,就必須保證對應的字段長度足夠大,至少能存儲512個字符;若是開發者是將access_token存儲在內存中,那什麼都不須要修改。

轉載自:http://www.csdn.net/article/2014-08-15/2821220-weixin

相關文章
相關標籤/搜索