Java开发微信公众号(五)---微信开发中如何获取access_token以及缓存access_token

获取access_token是微信api最重要的一个部分,因为调用其他api很多都需要用到access_token。比如自定义菜单接口、客服接口、获取用户信息接口、用户分组接口、群发接口等在请求的时候都需要用到access_token。

(一)access_token的介绍

       access_token是公众号的全局唯一接口调用凭据,公众号调用各接口时都需使用access_token。开发者需要进行妥善保存。access_token的存储至少要保留512个字符空间。access_token的有效期目前为2个小时,需时刷新,重复获取将导致上次获取的access_token失效。

      公众号可以使用AppID和AppSecret调用本接口来获取access_token。AppID和AppSecret可在微信公众平台官网-开发者中心页中获得。且一个公众号每天获取access_token的次数上限为2000次。详情请查看微信开发文档“获取access_token”

      通过开发者文档我们可以发现access_token的有效期限是2个小时,刷新的时候会导致上次获取的access_token失效,而且一个公众号每天获取access_token的次数上限为2000次。如果我们不做控制,而是选择各个业务逻辑点各自去刷新access_token,那么就可能会产生冲突,导致服务不稳定。所以我们需要有一个地方来保存我们获取到的access_token 和获取时间,每次有请求的时候先到这个地方查看是否已有 access_token,并判断这个access_token是否在有效期内,在的话直接返回,反之则重新调用接口获取access_token,同时在我们保存access_token的地方更新token和时间。

先和大家说一下我保存access_token的思路(目前先实现token的获取,access_token的缓存和更新后期会加上):

1.首先定义一个存放token的数据库表

2.首先,用户在调用需要access_token接口的时候,先查询数据库里保存access_token的值是否存在。

3.如果access_token存在的话,判断此access_token是否有效。如果有效的话,直接返回此值。

4.如果没有效,则调用获取access_token的接口,再次获取,并且更改数据库表中已经存在的access_token值 和 时间。

5.接第一步骤,如果access_token不存在,则调用获取access_token的接口,将获取到的数据保存在数据库表里。

 正常情况下,微信会返回下述JSON数据包给公众号:

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

错误时微信会返回错误码等信息,JSON数据包示例如下(该示例为AppID无效错误):

{"errcode":40013,"errmsg":"invalid appid"}

封装access_token类  Token

 1 package com.webchat.util.weixin.model;
 2 
 3 /**
 4  * token凭证 签名
 5  * 
 6  * @author Administrator
 7  *
 8  */
 9 public class Token {
10     //接口访问凭证
11     private String accessToken;
12     //有效期限
13     private int expiresIn;
14     //获取token的最新时间
15     private long addTime;
16     //签名
17     private String ticket;
18     public String getAccessToken() {
19         return accessToken;
20     }
21     public void setAccessToken(String accessToken) {
22         this.accessToken = accessToken;
23     }
24     public int getExpiresIn() {
25         return expiresIn;
26     }
27     public void setExpiresIn(int expiresIn) {
28         this.expiresIn = expiresIn;
29     }
30     public long getAddTime() {
31         return addTime;
32     }
33     public void setAddTime(long addTime) {
34         this.addTime = addTime;
35     }
36     public String getTicket() {
37         return ticket;
38     }
39     public void setTicket(String ticket) {
40         this.ticket = ticket;
41     }
42 }
View Code

 接口调用请求地址

https请求方式: GET
https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET
参数是否必须说明
grant_type 获取access_token填写client_credential
appid 第三方用户唯一凭证
secret 第三方用户唯一凭证密钥,即appsecret

 

 

    

 对于https请求,我们需要一个证书信任管理器,这个管理器类需要自己定义,但需要实现X509TrustManager接口,首先定义一个MyX509TrustManager 类。

 1 package com.webchat.util.weixin.utils;
 2 
 3 import java.security.cert.CertificateException;
 4 import java.security.cert.X509Certificate;
 5 
 6 import javax.net.ssl.X509TrustManager;
 7 
 8 public class MyX509TrustManager implements X509TrustManager{
 9 
10     public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
11         // TODO Auto-generated method stub
12 
13     }
14 
15     public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
16         // TODO Auto-generated method stub
17 
18     }
19 
20     public X509Certificate[] getAcceptedIssuers() {
21         // TODO Auto-generated method stub
22         return null;
23     }
24 
25 }
View Code

定义一个weixin.properties 文件

#wei xin pei zhi
wx_token=mywebchat

wx_appid=wx6138e8XXXXXXX
wx_secret=4364283f8XXXXXXXXXXXX

读取配置文件的工具类

 1 package com.webchat.util.weixin;
 2 
 3 import java.io.IOException;
 4 import java.util.Properties;
 5 
 6 import org.apache.log4j.Logger;
 7 import org.springframework.core.io.ClassPathResource;
 8 import org.springframework.core.io.Resource;
 9 import org.springframework.core.io.support.PropertiesLoaderUtils;
10 
11 /**
12  * 读取配置文件工具类
13  * @author Administrator
14  *
15  */
16 public class ConfigUtil {
17 
18     private static final Logger LOG = Logger.getLogger(ConfigUtil.class);
19 
20     private static Properties config = null;
21 
22     /**
23      * 返回weixin.properties配置信息
24      * @param key key值
25      * @return value值
26      */
27     public static String getProperty(String key) {
28         if (config == null) {
29             synchronized (ConfigUtil.class) {
30                 if (null == config) {
31                     try {
32                         Resource resource = new ClassPathResource("static/weixin/weixin.properties");
33                         config = PropertiesLoaderUtils.loadProperties(resource);
34                     } catch (IOException e) {
35                         LOG.error(e.getMessage(), e);
36                     }
37                 }
38             }
39         }
40 
41         return config.getProperty(key);
42     }
43 }
View Code

获取access_token的工具类:WechatConfig

  1 package com.webchat.util.weixin;
  2 
  3 import java.io.BufferedReader;
  4 import java.io.InputStream;
  5 import java.io.InputStreamReader;
  6 import java.io.OutputStream;
  7 import java.net.ConnectException;
  8 import java.net.URL;
  9 import java.util.HashMap;
 10 import java.util.Map;
 11 
 12 import javax.net.ssl.HttpsURLConnection;
 13 import javax.net.ssl.SSLContext;
 14 import javax.net.ssl.SSLSocketFactory;
 15 import javax.net.ssl.TrustManager;
 16 
 17 import com.alibaba.fastjson.JSONObject;
 18 import com.webchat.util.weixin.model.Token;
 19 import com.webchat.util.weixin.utils.MyX509TrustManager;
 20 
 21 public class WebChatConfig {
 22     // 获取access_token的接口地址(GET) 限2000(次/天)
 23     public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
 24     // 创建菜单
 25     public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
 26     // 查询自定义菜单
 27     public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
 28     // 删除自定义菜单
 29     public static final String MENU_DELTE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
 30     // 获取jsapi_ticket的接口地址(GET) 限2000(次/天)
 31     public static final String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
 32     // 发送模板消息
 33     public static final String SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
 34 
 35     // 微信appid
 36     public final static String APPID = ConfigUtil.getProperty("wx_appid");
 37     // 微信wx_secret
 38     public final static String SECERT = ConfigUtil.getProperty("wx_secret");
 39     
 40     
 41     
 42     public static void main(String[] args) {
 43         System.out.println(getToken(APPID, SECERT));
 44     }
 45 
 46     /**
 47      * 获得Token
 48      * 
 49      * @param appId
 50      * @param secret
 51      * @return
 52      */
 53     public static String getToken(String appId, String secret) {
 54         Token accessTocken = getToken(appId, secret, System.currentTimeMillis() / 1000);
 55         return accessTocken.getAccessToken();
 56     }
 57 
 58     /**
 59      * 获取access_token
 60      *
 61      * @param appid
 62      *            凭证
 63      * @param appsecret
 64      *            密钥
 65      * @return
 66      */
 67     public static Token getToken(String appid, String appsecret, long currentTime) {
 68         Token Token = null;
 69         // 调用接口获取token
 70         String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);
 71         JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
 72         // 如果请求成功
 73         if (null != jsonObject) {
 74             Token = new Token();
 75             Token.setAccessToken(jsonObject.getString("access_token"));
 76             // 正常过期时间是7200秒,此处设置3600秒读取一次
 77             // 一天有获取2000次的限制 ,设置1小时获取一次AccessToken防止超出请求限制
 78             Token.setExpiresIn(jsonObject.getIntValue("expires_in") / 2);
 79             Token.setAddTime(currentTime);
 80         }
 81         return Token;
 82     }
 83 
 84     /**
 85      * 发起https请求并获取结果
 86      *
 87      * @param requestUrl
 88      *            请求地址
 89      * @param requestMethod
 90      *            请求方式(GET、POST)
 91      * @param outputStr
 92      *            提交的数据
 93      * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
 94      */
 95     private static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
 96         JSONObject jsonObject = null;
 97         StringBuffer buffer = new StringBuffer();
 98         try {
 99             // 创建SSLContext对象,并使用我们指定的信任管理器初始化
100             TrustManager[] tm = { new MyX509TrustManager() };
101             SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
102             sslContext.init(null, tm, new java.security.SecureRandom());
103             // 从上述SSLContext对象中得到SSLSocketFactory对象
104             SSLSocketFactory ssf = sslContext.getSocketFactory();
105 
106             URL url = new URL(requestUrl);
107             HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
108             httpUrlConn.setSSLSocketFactory(ssf);
109 
110             httpUrlConn.setDoOutput(true);
111             httpUrlConn.setDoInput(true);
112             httpUrlConn.setUseCaches(false);
113             // 设置请求方式(GET/POST)
114             httpUrlConn.setRequestMethod(requestMethod);
115 
116             if ("GET".equalsIgnoreCase(requestMethod))
117                 httpUrlConn.connect();
118 
119             // 当有数据需要提交时
120             if (null != outputStr) {
121                 OutputStream outputStream = httpUrlConn.getOutputStream();
122                 // 注意编码格式,防止中文乱码
123                 outputStream.write(outputStr.getBytes("UTF-8"));
124                 outputStream.close();
125             }
126             // 将返回的输入流转换成字符串
127             InputStream inputStream = httpUrlConn.getInputStream();
128             InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
129             BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
130 
131             String str = null;
132             while ((str = bufferedReader.readLine()) != null) {
133                 buffer.append(str);
134             }
135             bufferedReader.close();
136             inputStreamReader.close();
137             // 释放资源
138             inputStream.close();
139             inputStream = null;
140             httpUrlConn.disconnect();
141             jsonObject = JSONObject.parseObject(buffer.toString());
142             // jsonObject = JSONObject.fromObject(buffer.toString());
143         } catch (ConnectException ce) {
144             System.out.println("Weixin server connection timed out.");
145         } catch (Exception e) {
146             System.out.println("https request error:{}" + e.getMessage());
147         }
148         return jsonObject;
149     }
150 }
View Code

运行里面的main方法查看获取的值即可

好了,到此获取access_token的方法基本结束了

有什么问题请指正^.^

如果在操作过程中有问题,欢迎随时讨论^.^

 

 

 

 

 

 

其他文章关联

(一)Java开发微信公众号(一)---初识微信公众号以及环境搭建

(二)Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发

(三)Java开发微信公众号(三)---微信服务器请求消息,响应消息,事件消息以及工具处理类的封装

(四)Java开发微信公众号(四)---微信服务器post消息体的接收及消息的处理

(五)Java开发微信公众号(五)---微信开发中如何获取access_token以及缓存access_token

 

posted @ 2018-09-10 12:50  HanZongBo  阅读(5474)  评论(2编辑  收藏  举报