代码中的XXX 是自己的开发者账号申请的appid和secret
/**
*
*/
package cn.hofan.weixin.controllers;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.net.URL;
import java.util.Timer;
import java.util.TimerTask;
import net.sf.json.JSONObject;
import org.apache.log4j.Logger;
import cn.hofan.spat.mvc2.ActionResult;// 公司内部MVC框架,可以使用Spring的MVC代替
import cn.hofan.spat.mvc2.annotation.Path;// 公司内部的MVC框架,可以使用Spring的MVC代替
import cn.hofan.weixin.util.HttpUtil;
import cn.hofan.weixin.util.SignUtil;
/**
* 微信 access_token 管理
*
* @author Aaron·Li
*
*/
@Path("token")
public class TokenController extends CoreController {
private static Logger log = Logger.getLogger(TokenController.class);
/**
* access_token 基础接口token信息
*/
private static String ACCESS_TOKEN = null;
/**
* access_token 基础接口token超时时间
*/
private static Long TOKEN_TIMEOUT = 0L;
/**
* access_token 基础接口token有效时间(毫秒)
*/
private static long ACCESS_TOKEN_TIME = 7000000;
/**
* 检查 access_token 是否超时的定时器
*/
private static Timer tirTokenCheck = null;
/**
* 定时器扫描间隔(5秒钟扫描一次)
*/
private static long timerPeriod = 5000;
/**
* 网页JSAPI接口jsapi_ticket
*/
private static String JSAPI_TICKET = null;
/**
* 网页JSAPI接口jsapi_ticket 超时时间
*/
private static Long JSAPI_TICKET_TIMEOUT = 0L;
/**
* 网页JSAPI接口jsapi_ticket 有效时间(毫秒)
*/
private static long JSAPI_TICKET_TIME = 7000000;
/**
* 基础接口 access_token 获取地址
*/
private static final String ACCESS_TOKEN_URL = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=XXX&secret=XXX";
/**
* 网页JSAPI接口 jsapi_ticket 获取地址
*/
private static final String JSAPI_TICKET_URL = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=%s&type=jsapi";
/**
* 基础接口、JSAPI接口token 集合
*/
private static JSONObject tokenInfo = new JSONObject();
/**
*
* @return
*/
@Path("gettoken")
public ActionResult getToken() {
try {
synchronized (tokenInfo) {
if (System.currentTimeMillis() >= TOKEN_TIMEOUT) {
initToken();
}
tokenInfo.put("sign", createSign(getParameter("url")));// 签名信息
}
} catch (Exception e) {
tokenInfo.put("error", e.getMessage());
log.error("获取 token异常", e);
e.printStackTrace();
}
return this.write(tokenInfo);
}
/**
* 初始化 JSAPI_TICKET,ACCESS_TOKEN 数据
*
* @throws Exception
* @throws IOException
*/
private static void initToken() throws Exception {
if (tirTokenCheck == null) {
tirTokenCheck = new Timer();
tirTokenCheck.schedule(new TimerTask() {
@Override
public void run() {
try {
initToken();
} catch (Exception e) {
log.error("定时器执行 initToken() 异常", e);
}
}
}, timerPeriod * 12, timerPeriod);// 1分钟后执行,每分钟执行一次initToken();
}
// 检查token 是否超时
// ACCESS_TOKEN 为主要的token,如果主的失效,从的也需要重新获得
if ((System.currentTimeMillis() >= TOKEN_TIMEOUT) || (System.currentTimeMillis() >= JSAPI_TICKET_TIMEOUT)) {
// 为防止服务器意外终止,先读取文件里的token信息 BEGIN
URL url = TokenController.class.getResource("/");// classes 目录
File f = null;
if (url != null) {
f = new File(url.getPath() + "/token.token");
if (!f.exists()) {
f.createNewFile();
} else {
FileReader fr = new FileReader(f);
BufferedReader br = new BufferedReader(fr);
String str = br.readLine();
br.close();
fr.close();
if (str != null && !str.isEmpty()) {
JSONObject json = JSONObject.fromObject(str);
if (json.containsKey("ACCESS_TOKEN")) {
ACCESS_TOKEN = json.getJSONObject("ACCESS_TOKEN").getString("access_token");
TOKEN_TIMEOUT = json.getJSONObject("ACCESS_TOKEN").getLong("TOKEN_TIMEOUT");
ACCESS_TOKEN_TIME = json.getJSONObject("ACCESS_TOKEN").getLong("expires_in") * 1000;// token 有效时间,服务器返回的是秒,需要转换为毫秒
JSAPI_TICKET = json.getJSONObject("JSAPI_TICKET").getString("ticket");
JSAPI_TICKET_TIMEOUT = json.getJSONObject("JSAPI_TICKET").getLong("JSAPI_TICKET_TIMEOUT");
JSAPI_TICKET_TIME = json.getJSONObject("JSAPI_TICKET").getLong("expires_in") * 1000;// token 有效时间,服务器返回的是秒,需要转换为毫秒
tokenInfo.put("ACCESS_TOKEN", json.getJSONObject("ACCESS_TOKEN"));
// JSAPI 调用需要用到的token
tokenInfo.put("JSAPI_TICKET", json.getJSONObject("JSAPI_TICKET"));
} else if (json.containsKey("error")) {
TOKEN_TIMEOUT = 0L;
}
}
}
}
// END
boolean isModify=false;// 是否更新文件信息
if (System.currentTimeMillis() >= TOKEN_TIMEOUT) {// 主token
ACCESS_TOKEN = null;
TOKEN_TIMEOUT = 0L;
long begin = System.currentTimeMillis();
setAccesToken();
long end = System.currentTimeMillis() - begin;
TOKEN_TIMEOUT = System.currentTimeMillis() + ACCESS_TOKEN_TIME - end;
isModify=true;
tokenInfo.getJSONObject("ACCESS_TOKEN").put("TOKEN_TIMEOUT", TOKEN_TIMEOUT);
}
if(System.currentTimeMillis() >= JSAPI_TICKET_TIMEOUT){// JSAPI token
JSAPI_TICKET = null;
JSAPI_TICKET_TIMEOUT = 0L;
long begin = System.currentTimeMillis();
setJsTicket();
long end = System.currentTimeMillis() - begin;
JSAPI_TICKET_TIMEOUT = System.currentTimeMillis() + JSAPI_TICKET_TIME - end;
isModify=true;
tokenInfo.getJSONObject("JSAPI_TICKET").put("JSAPI_TICKET_TIMEOUT", JSAPI_TICKET_TIMEOUT);
}
if(isModify){
// 写入文件
FileWriter fw = new FileWriter(f);
fw.flush();
fw.write(tokenInfo.toString());
fw.close();
}
}
}
/**
* 获取 ACCESS_TOKEN 并设置值
*
* @throws Exception
*/
private static void setAccesToken() throws Exception {
String ret = HttpUtil.get(ACCESS_TOKEN_URL, "UTF-8");
if (ret != null) {
JSONObject json = JSONObject.fromObject(ret);
if (json.containsKey("access_token")) {
ACCESS_TOKEN = json.getString("access_token");
ACCESS_TOKEN_TIME = json.getLong("expires_in") * 1000;// token 有效时间,服务器返回的是秒,需要转换为毫秒
tokenInfo.put("ACCESS_TOKEN", json);
} else if (json.containsKey("errcode")) {
throw new Exception(json.getString("errmsg"));
}
}
}
/**
* 获取 JSAPI_TICKET 并设置值
*
* @throws Exception
*/
private static void setJsTicket() throws Exception {
String ret = HttpUtil.get(String.format(JSAPI_TICKET_URL, ACCESS_TOKEN), "UTF-8");
if (ret != null) {
JSONObject json = JSONObject.fromObject(ret);
if (json.containsKey("ticket")) {
JSAPI_TICKET = json.getString("ticket");
JSAPI_TICKET_TIME = json.getLong("expires_in") * 1000;// token 有效时间,服务器返回的是秒,需要转换为毫秒
// JSAPI 调用需要用到的token
tokenInfo.put("JSAPI_TICKET", json);
} else if (json.containsKey("errcode")) {
throw new Exception(json.getString("errmsg"));
}
}
}
/**
* 生成签名信息
* @param requertUrl 待签名的url
* @return
*/
private JSONObject createSign(String requertUrl){
return JSONObject.fromObject(SignUtil.sign(JSAPI_TICKET, requertUrl));
}
@Override
protected void finalize() throws Throwable {
if (tirTokenCheck != null) {
tirTokenCheck.cancel();
tirTokenCheck = null;
}
super.finalize();
}
/**
* 获得基础接口 token
* @return
*/
public synchronized static String getACCESS_TOKEN() {
if(ACCESS_TOKEN==null){
try {
initToken();
} catch (Exception e) {
e.printStackTrace();
}
}
return ACCESS_TOKEN;
}
}