获取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
package com.webchat.util.weixin.model; /**
* token凭证 签名
*
* @author Administrator
*
*/
public class Token {
//接口访问凭证
private String accessToken;
//有效期限
private int expiresIn;
//获取token的最新时间
private long addTime;
//签名
private String ticket;
public String getAccessToken() {
return accessToken;
}
public void setAccessToken(String accessToken) {
this.accessToken = accessToken;
}
public int getExpiresIn() {
return expiresIn;
}
public void setExpiresIn(int expiresIn) {
this.expiresIn = expiresIn;
}
public long getAddTime() {
return addTime;
}
public void setAddTime(long addTime) {
this.addTime = addTime;
}
public String getTicket() {
return ticket;
}
public void setTicket(String ticket) {
this.ticket = ticket;
}
}
接口调用请求地址
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 类。
package com.webchat.util.weixin.utils; import java.security.cert.CertificateException;
import java.security.cert.X509Certificate; import javax.net.ssl.X509TrustManager; public class MyX509TrustManager implements X509TrustManager{ public void checkClientTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub } public void checkServerTrusted(X509Certificate[] chain, String authType) throws CertificateException {
// TODO Auto-generated method stub } public X509Certificate[] getAcceptedIssuers() {
// TODO Auto-generated method stub
return null;
} }
定义一个weixin.properties 文件
#wei xin pei zhi
wx_token=mywebchat wx_appid=wx6138e8XXXXXXX
wx_secret=4364283f8XXXXXXXXXXXX
读取配置文件的工具类
package com.webchat.util.weixin; import java.io.IOException;
import java.util.Properties; import org.apache.log4j.Logger;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PropertiesLoaderUtils; /**
* 读取配置文件工具类
* @author Administrator
*
*/
public class ConfigUtil { private static final Logger LOG = Logger.getLogger(ConfigUtil.class); private static Properties config = null; /**
* 返回weixin.properties配置信息
* @param key key值
* @return value值
*/
public static String getProperty(String key) {
if (config == null) {
synchronized (ConfigUtil.class) {
if (null == config) {
try {
Resource resource = new ClassPathResource("static/weixin/weixin.properties");
config = PropertiesLoaderUtils.loadProperties(resource);
} catch (IOException e) {
LOG.error(e.getMessage(), e);
}
}
}
} return config.getProperty(key);
}
}
获取access_token的工具类:WechatConfig
package com.webchat.util.weixin; import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.ConnectException;
import java.net.URL;
import java.util.HashMap;
import java.util.Map; import javax.net.ssl.HttpsURLConnection;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager; import com.alibaba.fastjson.JSONObject;
import com.webchat.util.weixin.model.Token;
import com.webchat.util.weixin.utils.MyX509TrustManager; public class WebChatConfig {
// 获取access_token的接口地址(GET) 限2000(次/天)
public final static String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=APPID&secret=APPSECRET";
// 创建菜单
public static final String MENU_CREATE_URL = "https://api.weixin.qq.com/cgi-bin/menu/create?access_token=ACCESS_TOKEN";
// 查询自定义菜单
public static final String MENU_GET_URL = "https://api.weixin.qq.com/cgi-bin/menu/get?access_token=ACCESS_TOKEN";
// 删除自定义菜单
public static final String MENU_DELTE_URL = "https://api.weixin.qq.com/cgi-bin/menu/delete?access_token=ACCESS_TOKEN";
// 获取jsapi_ticket的接口地址(GET) 限2000(次/天)
public static final String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=ACCESS_TOKEN&type=jsapi";
// 发送模板消息
public static final String SEND_MESSAGE = "https://api.weixin.qq.com/cgi-bin/message/template/send?access_token=ACCESS_TOKEN"; // 微信appid
public final static String APPID = ConfigUtil.getProperty("wx_appid");
// 微信wx_secret
public final static String SECERT = ConfigUtil.getProperty("wx_secret"); public static void main(String[] args) {
System.out.println(getToken(APPID, SECERT));
} /**
* 获得Token
*
* @param appId
* @param secret
* @return
*/
public static String getToken(String appId, String secret) {
Token accessTocken = getToken(appId, secret, System.currentTimeMillis() / 1000);
return accessTocken.getAccessToken();
} /**
* 获取access_token
*
* @param appid
* 凭证
* @param appsecret
* 密钥
* @return
*/
public static Token getToken(String appid, String appsecret, long currentTime) {
Token Token = null;
// 调用接口获取token
String requestUrl = ACCESS_TOKEN_URL.replace("APPID", appid).replace("APPSECRET", appsecret);
JSONObject jsonObject = httpRequest(requestUrl, "GET", null);
// 如果请求成功
if (null != jsonObject) {
Token = new Token();
Token.setAccessToken(jsonObject.getString("access_token"));
// 正常过期时间是7200秒,此处设置3600秒读取一次
// 一天有获取2000次的限制 ,设置1小时获取一次AccessToken防止超出请求限制
Token.setExpiresIn(jsonObject.getIntValue("expires_in") / 2);
Token.setAddTime(currentTime);
}
return Token;
} /**
* 发起https请求并获取结果
*
* @param requestUrl
* 请求地址
* @param requestMethod
* 请求方式(GET、POST)
* @param outputStr
* 提交的数据
* @return JSONObject(通过JSONObject.get(key)的方式获取json对象的属性值)
*/
private static JSONObject httpRequest(String requestUrl, String requestMethod, String outputStr) {
JSONObject jsonObject = null;
StringBuffer buffer = new StringBuffer();
try {
// 创建SSLContext对象,并使用我们指定的信任管理器初始化
TrustManager[] tm = { new MyX509TrustManager() };
SSLContext sslContext = SSLContext.getInstance("SSL", "SunJSSE");
sslContext.init(null, tm, new java.security.SecureRandom());
// 从上述SSLContext对象中得到SSLSocketFactory对象
SSLSocketFactory ssf = sslContext.getSocketFactory(); URL url = new URL(requestUrl);
HttpsURLConnection httpUrlConn = (HttpsURLConnection) url.openConnection();
httpUrlConn.setSSLSocketFactory(ssf); httpUrlConn.setDoOutput(true);
httpUrlConn.setDoInput(true);
httpUrlConn.setUseCaches(false);
// 设置请求方式(GET/POST)
httpUrlConn.setRequestMethod(requestMethod); if ("GET".equalsIgnoreCase(requestMethod))
httpUrlConn.connect(); // 当有数据需要提交时
if (null != outputStr) {
OutputStream outputStream = httpUrlConn.getOutputStream();
// 注意编码格式,防止中文乱码
outputStream.write(outputStr.getBytes("UTF-8"));
outputStream.close();
}
// 将返回的输入流转换成字符串
InputStream inputStream = httpUrlConn.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "utf-8");
BufferedReader bufferedReader = new BufferedReader(inputStreamReader); String str = null;
while ((str = bufferedReader.readLine()) != null) {
buffer.append(str);
}
bufferedReader.close();
inputStreamReader.close();
// 释放资源
inputStream.close();
inputStream = null;
httpUrlConn.disconnect();
jsonObject = JSONObject.parseObject(buffer.toString());
// jsonObject = JSONObject.fromObject(buffer.toString());
} catch (ConnectException ce) {
System.out.println("Weixin server connection timed out.");
} catch (Exception e) {
System.out.println("https request error:{}" + e.getMessage());
}
return jsonObject;
}
}
运行里面的main方法查看获取的值即可
好了,到此获取access_token的方法基本结束了
有什么问题请指正^.^
如果在操作过程中有问题,欢迎随时讨论^.^
其他文章关联
(一)Java开发微信公众号(一)---初识微信公众号以及环境搭建
(二)Java开发微信公众号(二)---开启开发者模式,接入微信公众平台开发
(三)Java开发微信公众号(三)---微信服务器请求消息,响应消息,事件消息以及工具处理类的封装