Java开发微信公众号(五)---微信开发中如何获取access_token以及缓存access_token Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发 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的思路(目前我是在项目中定义了一个Map集合,后期可能会保存到数据库 或者 缓存到Redis中):

1,首先定义一个存放token的Map集合

    // 缓存添加的时间
    public static String cacheAddTime = null;
    // token缓存
    public static Map<String, Token> TOKEN_TICKET_CACHE = new HashMap<String, Token>();
    // token对应的key
    private static final String TOKEN = "token";

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

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

   // 从Map缓存中读取token
    private static Token getTokenTicket(String key) {
        if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
            return TOKEN_TICKET_CACHE.get(key);
        }
        return null;
    }

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

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

   // 更新Map缓存中token
    private static void updateToken(String key, Token accessTocken) {
        if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
            TOKEN_TICKET_CACHE.remove(key);// 从缓存中删除" + key + "成功
        }
        TOKEN_TICKET_CACHE.put(key, accessTocken);
        // 更新缓存修改的时间
        cacheAddTime = String.valueOf(accessTocken.getAddTime());
    }

 正常情况下,微信会返回下述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 /**
 22  * 公众平台通用接口工具类
 23  * 
 24  * @author Administrator
 25  *
 26  */
 27 public class WechatConfig {
 28     // 获取access_token的接口地址(GET) 限2000(次/天)
 29     public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
 30     // 创建菜单
 31     public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
 32     // 查询自定义菜单
 33     public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
 34     // 删除自定义菜单
 35     public static final String MENU_DELTE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
 36     // 获取jsapi_ticket的接口地址(GET) 限2000(次/天)
 37     public static final String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
 38     // 发送模板消息
 39     public static final String SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN";
 40 
 41     // 微信appid
 42     public final static String APPID = ConfigUtil.getProperty("wx_appid");
 43     // 微信wx_secret
 44     public final static String SECERT = ConfigUtil.getProperty("wx_secret");
 45     // 缓存添加的时间
 46     public static String cacheAddTime = null;
 47     // token,ticket缓存
 48     public static Map<String, Token> TOKEN_TICKET_CACHE = new HashMap<String, Token>();
 49     // token对应的key
 50     private static final String TOKEN = "token";
 51     // ticket对应的key
 52     private static final String TICKET = "ticket";
 53 
 54     public static void main(String[] args) {
 55         System.out.println(getToken(APPID,SECERT));
 56     }
 57     /**
 58      * 获得Token
 59      * @param appId
 60      * @param secret
 61      * @return
 62      */
 63     public static String getToken(String appId, String secret) {
 64         Token accessTocken = getToken(appId, secret, System.currentTimeMillis() / 1000);
 65         return accessTocken.getAccessToken();
 66     }
 67 
 68     /**
 69      * 获取access_token
 70      *
 71      * @param appid
 72      *            凭证
 73      * @param appsecret
 74      *            密钥
 75      * @return
 76      */
 77     public static Token getToken(String appid, String appsecret, long currentTime) {
 78         Token tockenCache = getTokenTicket(TOKEN);
 79         Token Token = null;
 80         // 缓存存在并且没过期
 81         if (tockenCache != null && (currentTime - tockenCache.getAddTime() <= tockenCache.getExpiresIn())) {
 82             return tockenCache;
 83         } else {
 84             // 缓存中token不存在或已过期
 85             String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);
 86             JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
 87             // 如果请求成功
 88             if (null != jsonObject) {
 89                 Token = new Token();
 90                 Token.setAccessToken(jsonObject.getString("access_token"));
 91                 // 正常过期时间是7200秒,此处设置3600秒读取一次
 92                 //一天有获取2000次的限制 ,设置1小时获取一次AccessToken防止超出请求限制
 93                 Token.setExpiresIn(jsonObject.getIntValue("expires_in") / 2);
 94                 Token.setAddTime(currentTime);
 95                 updateToken(TOKEN, Token);
 96             }
 97         }
 98         return Token;
 99     }
100 
101     /**
102      * 发起https请求并获取结果
103      *
104      * @param requestUrl
105      *            请求地址
106      * @param requestMethod
107      *            请求方式(GET、POST)
108      * @param outputStr
109      *            提交的数据
110      * @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
111      */
112     private static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
113         JSONObject jsonObject = null;
114         StringBuffer buffer = new StringBuffer();
115         try {
116             // 创建SSLContext对象,并使用我们指定的信任管理器初始化
117             TrustManager[] tm = { new MyX509TrustManager() };
118             SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
119             sslContext.init(null, tm, new java.security.SecureRandom());
120             // 从上述SSLContext对象中得到SSLSocketFactory对象
121             SSLSocketFactory ssf = sslContext.getSocketFactory();
122 
123             URL url = new URL(requestUrl);
124             HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
125             httpUrlConn.setSSLSocketFactory(ssf);
126 
127             httpUrlConn.setDoOutput(true);
128             httpUrlConn.setDoInput(true);
129             httpUrlConn.setUseCaches(false);
130             // 设置请求方式(GET/POST)
131             httpUrlConn.setRequestMethod(requestMethod);
132 
133             if ("GET".equalsIgnoreCase(requestMethod))
134                 httpUrlConn.connect();
135 
136             // 当有数据需要提交时
137             if (null != outputStr) {
138                 OutputStream outputStream = httpUrlConn.getOutputStream();
139                 // 注意编码格式,防止中文乱码
140                 outputStream.write(outputStr.getBytes("UTF-8"));
141                 outputStream.close();
142             }
143             // 将返回的输入流转换成字符串
144             InputStream inputStream = httpUrlConn.getInputStream();
145             InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
146             BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
147 
148             String str = null;
149             while ((str = bufferedReader.readLine()) != null) {
150                 buffer.append(str);
151             }
152             bufferedReader.close();
153             inputStreamReader.close();
154             // 释放资源
155             inputStream.close();
156             inputStream = null;
157             httpUrlConn.disconnect();
158             jsonObject = JSONObject.parseObject(buffer.toString());
159             // jsonObject = JSONObject.fromObject(buffer.toString());
160         } catch (ConnectException ce) {
161             System.out.println("Weixin server connection timed out.");
162         } catch (Exception e) {
163             System.out.println("https request error:{}" + e.getMessage());
164         }
165         return jsonObject;
166     }
167 
168     // 从缓存中读取token或者ticket
169     private static Token getTokenTicket(String key) {
170         if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
171             return TOKEN_TICKET_CACHE.get(key);
172         }
173         return null;
174     }
175 
176     // 更新缓存中token或者ticket
177     private static void updateToken(String key, Token accessTocken) {
178         if (TOKEN_TICKET_CACHE != null && TOKEN_TICKET_CACHE.get(key) != null) {
179             TOKEN_TICKET_CACHE.remove(key);// 从缓存中删除" + key + "成功
180         }
181         TOKEN_TICKET_CACHE.put(key, accessTocken);
182         // 更新缓存修改的时间
183         cacheAddTime = String.valueOf(accessTocken.getAddTime());
184     }
185 }
View Code

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

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

有什么问题请指正^.^

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

其他文章关联

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

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

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

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

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

猜你喜欢

转载自www.cnblogs.com/han108/p/9614898.html