简单的说,accessToken相当于一个通行证,根据微信公众号的规定,这个通行证的有效期时间是7200秒。在与微信交互的时候,得提前亮出这个通行证,微信拿去校验后,才会许可你的操作。
(1)根据微信的规定,开发者需要使用自己的appid和appsecret去向微信换取accessToken,并将得到的这个数据保存在自己的本地,以便后续交互时使用。
(2)根据上述要求,我们可以提取出一下几点:
根据上述结论分析,我们可以使用redis来存、取accessToken,因为redis存取快,而且,可以部署在独立的服务器上,如果存在服务器集群或者多个系统之间均需要使用这个accessToken的时候,均可以获取到这个对应的值。再者,使用quartz来定时与微信交互,刷新accessToken的值。
commons-httpclient
commons-httpclient
3.1
这一类的工具类,有很多,大家可以网上搜索一下
public static String sendPost(String url) throws HttpException, IOException {
// 创建httpClient实例对象
HttpClient httpClient = new HttpClient();
// 设置httpClient连接主机服务器超时时间:15000毫秒
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(2000);
// 创建post请求方法实例对象
PostMethod postMethod = new PostMethod(url);
// 设置post请求超时时间
postMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);
postMethod.addRequestHeader("Content-Type", "application/json");
httpClient.executeMethod(postMethod);
String result = postMethod.getResponseBodyAsString();
postMethod.releaseConnection();
return result;
}
public static String sendGet(String url) throws HttpException, IOException {
// 创建httpClient实例对象
HttpClient httpClient = new HttpClient();
// 设置httpClient连接主机服务器超时时间:15000毫秒
httpClient.getHttpConnectionManager().getParams().setConnectionTimeout(15000);
// 创建GET请求方法实例对象
GetMethod getMethod = new GetMethod(url);
// 设置post请求超时时间
getMethod.getParams().setParameter(HttpMethodParams.SO_TIMEOUT, 60000);
getMethod.addRequestHeader("Content-Type", "application/json");
httpClient.executeMethod(getMethod);
String result = getMethod.getResponseBodyAsString();
getMethod.releaseConnection();
return result;
}
①导入spring-boot-starter-quartz
②配置quartz的一些必要的属性,例如执行时间间隔等等
我这里偷个懒,直接在项目中写死,最好的方式是将其写在数据库里面,然后读取数据库的时间来执行,但是这个地方,我还没完全搞懂,所以,这里就不拿出来献丑了,取个简单的方法,先实现业务功能,我这里配置的是每五秒执行一次定时任务。
③考虑到一种情况,就是说,当有多个公众号的时候,其实跟微信交互的地址是一样的,唯一的区别再去传递的参数(比如appid、appsecret、accessToken这些不一致)不一致。这个时候,我考虑到将其与微信交互的地址,存入到数据库中去。由此新建一张表,保存与微信交互的地址。
这里列举了两个请求地址,trade_url为主要字段,在这里是根据trade_type来查询,获取对应的url。
④redis存取值
直接使用最简单的字符类型即可,并设置当前key的过期时间即为当前accessToken的有效期,但是这个有效时间要设置得稍微比7200秒小一点,得保证accessToken一直是有效的。
注入RedisTemplate
取值:
// accessToken值
String accessToken = redisTemplate.boundValueOps(p.getAppId()).get();
// 过期时间
Long lastTime = redisTemplate.getExpire(p.getAppId());
存值:
redisTemplate.boundValueOps(p.getAppId()).set(accessToken);
redisTemplate.expire(p.getAppId(), EXPIRE_TIME, TimeUnit.SECONDS);
这里是根据appid为key来存的
④创建一个RefreshJob类继承QuartzJobBean,并重写executeInternal()来实现自己的业务逻辑
XyyPublicPropertiesExample example = new XyyPublicPropertiesExample();
example.createCriteria().andIsDeleteEqualTo(0);
// 获取所有公众号
List publicList = xyyPublicPropertiesDao.selectByExample(example);
XyyPublicTradeExample tradeExample = new XyyPublicTradeExample();
tradeExample.createCriteria().andTradeTypeEqualTo(ConstantsForTradeType.ENUM_TRADE_TYPE.REFRESH_TOKEN.getCode());
List tradeTypes = xyyPublicTradeDao.selectByExample(tradeExample);
String requestUrl = "";
if (CollectionUtils.isNotEmpty(tradeTypes)) {
// 请求地址
requestUrl = tradeTypes.get(0).getTradeUrl();
}
for (XyyPublicProperties p:publicList) {
// accessToken值
String accessToken = redisTemplate.boundValueOps(p.getAppId()).get();
// 过期时间
Long lastTime = redisTemplate.getExpire(p.getAppId());
if (StringUtils.isEmpty(accessToken) || lastTime <= 200) {
// 不存在accessToken,或者剩余时间太少
// 2.获取新的accessToken
requestUrl = requestUrl + "&appid=" + p.getAppId() + "&secret=" + p.getAppSecret();
String result = "";
try {
result = HttpUtils.sendGet(requestUrl);
}catch (IOException e) {
e.printStackTrace();
result = "";
}
logger.info("result:{}", result);
if (StringUtils.isNotEmpty(result)) {
JSONObject jsonObject = JSON.parseObject(result);
accessToken = jsonObject.getString("access_token");
if (StringUtils.isNotEmpty(accessToken)) {
int expires_in = jsonObject.getInteger("expires_in");
redisTemplate.boundValueOps(p.getAppId()).set(accessToken);
redisTemplate.expire(p.getAppId(), EXPIRE_TIME, TimeUnit.SECONDS);
logger.info("公众号:{}刷新token完成,token:{}", p.getAppId(), accessToken);
} else{
String errmsg = jsonObject.getString("errmsg");
}
}
} else {
logger.info("公众号:{}刷新token时间未到", p.getAppId());
}
}
}
简单的说,逻辑如下:
1.获取需要刷新token的公众号
2.根据appid查询redis中存储的value值以及到期时间
3.若value值不存在,或者到期时间小于200秒,则组url调微信的接口去获取新的accessToken并存储在本地,否则则认为当前不需要刷新accessToken。
组url是比较简单的,就是字符串的连接,再根据微信获取的结果,来判断是否成功、并取值处理即可。
本期关于accessToken的存取,就分享到这里了,下期将继续分享微信消息体的解析及响应。