为什么要缓存token?
这里的token指的是微信JSAPI中基础支持的ACCESS_TOKEN,并非网页授权ACCESS_TOKEN。网页授权Token每天的调用次数没有限制,不需要缓存。
接口每日限额
获取access_token
2000
自定义菜单创建
1000
自定义菜单查询
10000
获取用户基本信息
5000000
获取网页授权access_token
无
刷新网页授权access_token
无
网页授权获取用户信息
无
从上面的表格我们可以看到,微信基础支持的token每天调用频次为2000次。而token的有效时间为7200s,当实现了token的全局缓存后,理论每天只需要调用12次。相反,2000次即使仅供微信分享回调,在有一定用户基础的项目中完全满足不了。
缓存方案
项目启动时开启一个定时器,每7180s执行一次Http请求,从微信获取最新的access_token并将redis中旧的access_token替换掉。
代码中有需要使用access_token时,直接从缓存中读取。
Spring Boot使用定时器
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.sql.SQLException;
import javax.annotation.Resource;
import org.apache.log4j.Logger;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import com.bjb.dao.impl.RedisTokenHelper;
import net.sf.json.JSONObject;
/**
* 全局定时器
* @author qiqj
*
*/
@Component
public class Scheduler {
private final Logger logger = Logger.getRootLogger();
@Resource
private RedisTokenHelper redisTokenHelper;
/**
* 定时获取access_token
* @throws SQLException
*/
@Scheduled(fixedDelay=7180000)
public void getAccessToken() throws SQLException{
logger.info("==============开始获取access_token===============");
String access_token = null;
String grant_type = "client_credential";
String AppId= WxPropertiseUtil.getProperty("appid");
String secret= WxPropertiseUtil.getProperty("secret");
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type="+grant_type+"&appid="+AppId+"&secret="+secret;
try {
URL urlGet = new URL(url);
HttpURLConnection http = (HttpURLConnection) urlGet.openConnection();
http.setRequestMethod("GET"); // 必须是get方式请求
http.setRequestProperty("Content-Type","application/x-www-form-urlencoded");
http.setDoOutput(true);
http.setDoInput(true);
http.connect();
InputStream is = http.getInputStream();
int size = is.available();
byte[] jsonBytes = new byte[size];
is.read(jsonBytes);
String message = new String(jsonBytes, "UTF-8");
JSONObject demoJson = JSONObject.fromObject(message);
//System.out.println("JSON字符串:"+demoJson);
access_token = demoJson.getString("access_token");
is.close();
logger.info("==============结束获取access_token===============");
} catch (Exception e) {
e.printStackTrace();
}
logger.info("==============开始写入access_token===============");
redisTokenHelper.saveObject("global_token", access_token);
logger.info("==============写入access_token成功===============");
}
}
读取配置文件工具类
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
import org.apache.log4j.Logger;
/**
* 读取微信配置文件 wxconfig.properties工具类
*@author qiqj
*
*/
public class WxPropertiseUtil {
private static Properties properties = new Properties();
protected static final Logger logger = Logger.getRootLogger();
static{
InputStream in = WxPropertiseUtil.class.getClassLoader().getResourceAsStream("wxconfig.properties");
try {
properties.load(in);
} catch (IOException e) {
e.printStackTrace();
logger.error(e.getMessage());
}
}
public static String getProperty(String key){
return properties.getProperty(key);
}
public static void UpdateProperty(String key,String value){
properties.setProperty(key, value);
}
}
微信配置文件
appid=xxxx
secret=xxxxxx
Redis操作工具类
import java.util.concurrent.TimeUnit;
import javax.annotation.Resource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Repository;
/**
* 封装Redis存取Token对的工具类
*@author qiqj
*
*/
@Repository
public class RedisTokenHelper {
@Autowired
StringRedisTemplate stringRedisTemplate;
@Autowired
RedisTemplate redisTemplate;
@Resource(name="stringRedisTemplate")
ValueOperations ops;
@Resource(name="redisTemplate")
ValueOperations objOps;
/**
* 键值对存储 字符串 :有效时间3分钟
*@param tokenType Token的key
*@param Token Token的值
*/
public void save(String tokenType,String Token){
ops.set(tokenType, Token, 180, TimeUnit.SECONDS);
}
/**
* 根据key从redis获取value
*@param tokenType
*@return String
*/
public String getToken(String tokenType){
return ops.get(tokenType);
}
/**
* redis 存储一个对象
*@param key
*@param obj
*@param timeout 过期时间 单位:s
*/
public void saveObject(String key,Object obj,long timeout){
objOps.set(key, obj,timeout,TimeUnit.SECONDS);
}
/**
* redis 存储一个对象 ,不过期
*@param key
*@param obj
*/
public void saveObject(String key,Object obj){
objOps.set(key, obj);
}
/**
* 从redis取出一个对象
*@param key
*@param obj
*/
public Object getObject(String key){
return objOps.get(key);
}
/**
* 根据Key删除Object
*@param key
*/
public void removeObject(String key){
redisTemplate.delete(key);
}
}
简单测试
import org.apache.log4j.Logger;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.transaction.annotation.Transactional;
import com.bjb.Application;
import com.bjb.dao.impl.RedisTokenHelper;
/**
* Junit单元测试类
*@author qiqj
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes=Application.class)
@WebAppConfiguration
@Transactional
public class JUnitTest {
private final Logger logger = Logger.getRootLogger();
@Resource
private RedisTokenHelper redisTokenHelper;
@Test
public void test(){
String access_token = (String) redisTokenHelper.getObject("global_token");
System.out.println("access_token:"+access_token);
}
}