1. 程式人生 > >微信公眾號入門筆記(四)獲取access_token

微信公眾號入門筆記(四)獲取access_token

作者:zhutulang

以下是微信公眾平臺開發者文件中擷取的內容:

access_token是公眾號的全域性唯一票據,公眾號呼叫各介面時都需使用access_token。開發者需要進行妥善儲存。access_token的儲存至少要保留512個字元空間。access_token的有效期目前為2個小時,需定時重新整理,重複獲取將導致上次獲取的access_token失效。

介面呼叫請求說明

http請求方式: GET

引數說明

引數

是否必須

說明

grant_type

獲取access_token填寫client_credential

appid

第三方使用者唯一憑證

secret

第三方使用者唯一憑證金鑰,即appsecret

返回說明

正常情況下,微信會返回下述JSON資料包給公眾號:

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

引數

說明

access_token

獲取到的憑證

expires_in

憑證有效時間,單位:秒

那麼,從以上的說明中我們知道:

(1)我們需要以get方式傳送https請求。

(2)appid和secret 可以從我們的公眾號後臺檢視。

(3)目前,access_token的有效期目前為2個小時,我們需要提供一個定時重新整理機制。並且最好能有一個強制重新整理的機制。

一、如何傳送https請求

對於第一點,用HttpClient包傳送https請求,核心思路就是忽略校驗過程,程式碼參考自:

SSLClient 類如下:

package com.dongliushui.util;
 
importjava.security.cert.CertificateException;
 
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;
 
import org.apache.http.conn.ClientConnectionManager;
import org.apache.http.conn.scheme.Scheme;
importorg.apache.http.conn.scheme.SchemeRegistry;
importorg.apache.http.conn.ssl.SSLSocketFactory;
importorg.apache.http.impl.client.DefaultHttpClient;
 
/**
 *@ClassName: SSLClient
 *@Description: 用於進行Https請求的HttpClient 
 *@author (程式碼來源):http://blog.csdn.net/rongyongfeikai2/article/details/41659353
 *@date 2016年1月8日
 *@version V1.0
 */
public class SSLClient extendsDefaultHttpClient {
 
         publicSSLClient() throws Exception{ 
        super(); 
       SSLContext ctx = SSLContext.getInstance("TLS"); 
       X509TrustManager tm = new X509TrustManager() {
 
                            @Override
                            publicvoid checkClientTrusted(
                                               java.security.cert.X509Certificate[]chain, String authType)
                                               throwsCertificateException {
                                     //TODO Auto-generated method stub
                                    
                            }
 
                            @Override
                            publicvoid checkServerTrusted(
                                               java.security.cert.X509Certificate[]chain, String authType)
                                               throwsCertificateException {
                                     //TODO Auto-generated method stub
                                    
                            }
 
                            @Override
                            publicjava.security.cert.X509Certificate[] getAcceptedIssuers() {
                                     //TODO Auto-generated method stub
                                     returnnull;
                            } 
              
       }; 
       ctx.init(null, new TrustManager[]{tm}, null); 
       SSLSocketFactory ssf = newSSLSocketFactory(ctx,SSLSocketFactory.ALLOW_ALL_HOSTNAME_VERIFIER); 
       ClientConnectionManager ccm = this.getConnectionManager(); 
       SchemeRegistry sr = ccm.getSchemeRegistry(); 
       sr.register(new Scheme("https", 443, ssf)); 
   }       
}

HttpUtil 類如下:

package com.dongliushui.util;
 
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
 
import net.sf.json.JSONObject;
 
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
importorg.apache.http.client.entity.UrlEncodedFormEntity;
importorg.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
importorg.apache.http.impl.client.DefaultHttpClient;
importorg.apache.http.message.BasicNameValuePair;
import org.apache.http.util.EntityUtils;
 
/**
 *@ClassName: HttpUtil
 * @Description:Http請求工具類
 *@author zhutulang
 *@date 2016年1月8日
 *@version V1.0
 */
public class HttpUtil {
 
         /**
          * <p>Title: doHttpsPost</p>
          * <p>Description: 傳送https 形式的post請求</p>
          * @param url 請求url
          * @param contentType
          * @param paramMap 引數map
          * @return
          * @author zhutulang
          * @version 1.0
          */
         publicstatic byte[] doHttpsPostJson(String url, String contentType, Map<String,String> paramMap){
                   returnpostJson(1, url, contentType, paramMap);
         }
        
         /**
          * <p>Title: doHttpsPost</p>
          * <p>Description: 傳送http 形式的post請求</p>
          * @param url 請求url
          * @param contentType
          * @param paramMap 引數map
          * @return
          * @author zhutulang
          * @version 1.0
          */
         publicstatic byte[] doPostJson(String url, String contentType, Map<String,String> paramMap){
                  return postJson(0, url, contentType,paramMap);
         }
        
         /**
          * <p>Title: doHttpsGet</p>
          * <p>Description: 傳送https 形式的get請求</p>
          * @param url 請求url
          * @param contentType
          * @return
          * @author zhutulang
          * @version 1.0
          */
         publicstatic byte[] doHttpsGet(String url, String contentType){
                   returnget(1, url, contentType);
         }
        
         /**
          * <p>Title: doGet</p>
          * <p>Description: 傳送http 形式的gett請求</p>
          * @param url 請求url
          * @param contentType
          * @return
          * @author zhutulang
          * @version 1.0
          */
         publicstatic byte[] doGet(String url, String contentType){
                   returnget(0, url, contentType);
         }
        
         /**
          * <p>Title: post</p>
          * <p>Description: 傳送post請求,表單提交引數</p>
          * @param type 0:普通post請求  1:https形式的post請求
          * @param url 請求url
          * @param contentType 
          * @param paramMap 引數map
          * @return
          * @author zhutulang
          * @version 1.0
          */
         privatestatic byte[] postCommon(int type, String url, String contentType,Map<String, String> paramMap){
                   //響應內容
             byte[] bs = null;
            
             HttpClient httpClient = null;
             HttpPost httpPost = null;
            
             try {
                     
               if(type == 0){
                      //建立傳送 http 請求的httpClient例項
                           httpClient= new DefaultHttpClient();
               }else if(type == 1){
                      //建立傳送 https 請求的httpClient例項
                           httpClient= new SSLClient();
               }
              
               // 建立HttpPost
               httpPost = new HttpPost(url);
              httpPost.setHeader("content-type", contentType);
               //設定引數
               List<NameValuePair> list = newArrayList<NameValuePair>();
               if(paramMap != null){
                        Iterator<Entry<String, String>>iterator = paramMap.entrySet().iterator(); 
                   while(iterator.hasNext()){ 
                       Entry<String,String> elem =(Entry<String, String>) iterator.next(); 
                       list.add(newBasicNameValuePair(elem.getKey(),elem.getValue())); 
                   } 
                   if(list.size() > 0){ 
                       UrlEncodedFormEntity entity = newUrlEncodedFormEntity(list,"UTF-8"); 
                       httpPost.setEntity(entity); 
                   }
               }
               // 執行POST請求
               HttpResponse response =httpClient.execute(httpPost);
               // 獲取響應實體
               HttpEntity entity = response.getEntity();
               if(entity != null){
                        bs = EntityUtils.toByteArray(entity);
               }
             } catch (Exception e) {
               e.printStackTrace();
             } finally {
               // 關閉連線,釋放資源
              httpClient.getConnectionManager().shutdown();
               httpPost = null;
               httpClient = null;
             }
             return bs;
         }
        
         /**
          * <p>Title: post</p>
          * <p>Description: 傳送post請求,json方式提交引數</p>
          * @param type 0:普通post請求  1:https形式的post請求
          * @param url 請求url
          * @param contentType 
          * @param paramMap 引數map
          * @return
          * @author zhutulang
          * @version 1.0
          */
         privatestatic byte[] postJson(int type, String url, String contentType, Map<String,String> paramMap){
                   //響應內容
             byte[] bs = null;
            
             HttpClient httpClient = null;
             HttpPost httpPost = null;
            
             try {
                     
               if(type == 0){
                      //建立傳送 http 請求的httpClient例項
                           httpClient= new DefaultHttpClient();
               }else if(type == 1){
                      //建立傳送 https 請求的httpClient例項
                           httpClient= new SSLClient();
               }
              
               // 建立HttpPost
               httpPost = new HttpPost(url);
              httpPost.setHeader("content-type", contentType);
               if(paramMap != null){
                        Iterator<Entry<String, String>>iterator = paramMap.entrySet().iterator(); 
                        // 接收引數json列表 
              JSONObject jsonParam = newJSONObject(); 
                   while(iterator.hasNext()){ 
                       Entry<String,String> elem =(Entry<String, String>) iterator.next(); 
                       jsonParam.put(elem.getKey(),elem.getValue());   
                   } 
                   if(jsonParam.size() > 0){
                       StringEntity entity = newStringEntity(jsonParam.toString(),"UTF-8");
                 entity.setContentEncoding("UTF-8");   
                 entity.setContentType("application/json");   
                 httpPost.setEntity(entity);   
                   }
               }
               // 執行POST請求
               HttpResponse response =httpClient.execute(httpPost);
               // 獲取響應實體
               HttpEntity entity = response.getEntity();
               if(entity != null){
                        bs = EntityUtils.toByteArray(entity);
               }
             } catch (Exception e) {
               e.printStackTrace();
             } finally {
               // 關閉連線,釋放資源
              httpClient.getConnectionManager().shutdown();
               httpPost = null;
               httpClient = null;
             }
             return bs;
         }
        
         /**
          * <p>Title: get</p>
          * <p>Description: 傳送get請求</p>
          * @param type 0:普通get請求  1:https形式的get請求
          * @param url  請求url
          * @param contentType
          * @return
          * @author zhutulang
          * @version 1.0
          */
         privatestatic byte[] get(int type, String url, String contentType){
                   //響應內容
             byte[] bs = null;
            
             HttpClient httpClient = null;
             HttpGet httpGet = null;
            
             try {
               if(type == 0){
                      //建立傳送 http 請求的httpClient例項
                           httpClient= new DefaultHttpClient();
               }else if(type == 1){
                      //建立傳送 https 請求的httpClient例項
                           httpClient= new SSLClient();
               }
              
               // 建立HttpPost
               httpGet = new HttpGet(url);
              httpGet.setHeader("content-type", contentType);
               // 執行POST請求
               HttpResponse response =httpClient.execute(httpGet);
               // 獲取響應實體
               HttpEntity entity = response.getEntity();
               if(entity != null){
                        bs = EntityUtils.toByteArray(entity);
               }
             } catch (Exception e) {
               e.printStackTrace();
             } finally {
               // 關閉連線,釋放資源
               httpClient.getConnectionManager().shutdown();
               httpGet = null;
               httpClient = null;
             }
             return bs;
         }
}


二、如何定時重新整理access_token

在叢集環境中,這個問題可能會比較複雜。我們可能需要考慮到在叢集中各個機器的任務排程協調,對於獲取到的access_token,我們可能會考慮將它儲存在資料庫中,或者統一的快取模組中,比如redis中。對於單伺服器環境,我們大可以直接將其儲存在記憶體中。

定時任務我們經常會用到quartz框架。不過spring也提供有任務排程的模組,我習慣用@Scheduled註解。至於它的使用,大家可自行百度。

以下程式碼中形如@Value("#{weixinProperties['AppId']}")

是通過spring讀取配置檔案,如果沒見過這樣做的朋友也可以自行去查詢相關資料。

相關的配置放在一個名為weixin.properties的配置檔案中:

#weixin properties

# 你自己的appid和appsecret

AppId=XXXXXXXXX

AppSecret=XXXXXXXXXXXXXXXXXXX

#get access_token urlget

get_access_token_url=https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET

#batchget_material urlpost

batchget_material_url=https://api.weixin.qq.com/cgi-bin/material/batchget_material?access_token=ACCESS_TOKEN

Spring配置檔案中:

<!-- weixin.properties 配置檔案 -->
    <bean id="weixinProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
        <property name="locations">
            <list>
                <value>classpath*:weixin.properties</value>
            </list>
        </property>
    </bean>
    <bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer">
        <property name="properties" ref="weixinProperties" />
    </bean> 


AccessTokenTaker 程式碼如下:

package com.dongliushui.quartz;
 
importjava.io.UnsupportedEncodingException;
 
import org.apache.log4j.Logger;
importorg.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
importorg.springframework.stereotype.Component;
 
import com.dongliushui.util.HttpUtil;
 
/**
 *@ClassName: AccessTokenTaker
 *@Description: 獲取access_token
 *@author zhutulang
 *@date 2016年1月10日
 *@version V1.0
 */
@Component
public class AccessTokenTaker {
 
         @Value("#{weixinProperties['AppId']}")
         private  String appId;
        
         @Value("#{weixinProperties['AppSecret']}")
         private  String appSecret;
        
         @Value("#{weixinProperties['get_access_token_url']}")
         private  String getAccessTokenUrl;
        
         /**
          * access_token
          */
         privatestatic  String ACCESS_TOKEN = null;
         /**
          * 上次更新access_token時間
          */
         privatestatic  LongLAST_ACCESS_TOKEN_UPDATE_TIME = null;
        
         privatestatic Logger log = Logger.getLogger(AccessTokenTaker.class);
        
         /**
          * <p>Title: get</p>
          * <p>Description: 每隔一個小時去獲取一次access_token</p>
          * @author zhutulang
          * @version 1.0
          */
         @Scheduled(fixedRate=3600000)
         privatevoid getTask(){
                   get();
         }
        
         /**
          * <p>Title: getFromCache</p>
          * <p>Description: 從快取中獲取access_token</p>
          * @return
          * @author zhutulang
          * @version 1.0
          */
         public  String getFromCache(){
                   returnACCESS_TOKEN;
         }
        
         /**
          * <p>Title: getNew</p>
          * <p>Description: 強制更新、獲取access_token</p>
          * <p>如果發現現在的時間戳和上次更新的時間戳間隔小於5分鐘,那麼不更新</p>
          * @return
          * @author zhutulang
          * @version 1.0
          */
         publicsynchronized  String getNew(){
                   longtimeNow = System.currentTimeMillis();
                   if(LAST_ACCESS_TOKEN_UPDATE_TIME== null){
                            get();
                   }elseif(timeNow - LAST_ACCESS_TOKEN_UPDATE_TIME < 300000){
                            //如果是5分鐘以內
                            returnACCESS_TOKEN;
                   }else{
                            get();
                   }
                   returnACCESS_TOKEN;
         }
        
         /**
          * <p>Title: get</p>
          * <p>Description: 呼叫獲取access_token介面</p>
          * @author zhutulang
          * @version 1.0
          */
         synchronized  void get(){
                   Stringurl = getAccessTokenUrl.replace("APPID",appId).replace("APPSECRET", appSecret);
                   StringcontentType = "application/json";
                   byte[]bytes = HttpUtil.doHttpsGet(url, contentType);
                   try{
                            StringaccessToken = new String(bytes, "UTF-8");
                            longtimeNow = System.currentTimeMillis();
                            ACCESS_TOKEN= accessToken;
                            LAST_ACCESS_TOKEN_UPDATE_TIME= timeNow;
                            log.info("執行獲取access_token任務,access_token="+ACCESS_TOKEN);
                 log.info("時間戳="+LAST_ACCESS_TOKEN_UPDATE_TIME);
                   }catch (UnsupportedEncodingException e) {
                            //TODO Auto-generated catch block
                            e.printStackTrace();
                   }
         }
 
         publicString getAppId() {
                   returnappId;
         }
 
         publicvoid setAppId(String appId) {
                   this.appId= appId;
         }
 
         publicString getAppSecret() {
                   returnappSecret;
         }
 
         publicvoid setAppSecret(String appSecret) {
                   this.appSecret= appSecret;
         }
 
         publicString getGetAccessTokenUrl() {
                   returngetAccessTokenUrl;
         }
 
         publicvoid setGetAccessTokenUrl(String getAccessTokenUrl) {
                   this.getAccessTokenUrl= getAccessTokenUrl;
         }
}


 其它相關程式碼可檢視: