重构token实现机制
This commit is contained in:
@@ -41,4 +41,8 @@ weixin4j-base
|
||||
|
||||
* 2015-01-04
|
||||
|
||||
+ ConfigUtil类新增获取classpath目录下的资源路径的方法
|
||||
+ ConfigUtil类新增获取classpath目录下的资源路径的方法
|
||||
|
||||
* 2015-01-10
|
||||
|
||||
+ 重构token实现机制
|
||||
@@ -100,11 +100,16 @@ public class HttpRequest {
|
||||
StringBuilder sb = new StringBuilder(url);
|
||||
if (parameters != null && parameters.length > 0) {
|
||||
if (url.indexOf("?") < 0) {
|
||||
sb.append(String.format("?%s=%s", parameters[0].getName(),
|
||||
parameters[0].getValue()));
|
||||
sb.append("?");
|
||||
} else {
|
||||
sb.append("&");
|
||||
}
|
||||
for (int i = 0; i < parameters.length; i++) {
|
||||
sb.append(parameters[i].toGetPara());
|
||||
sb.append(String.format("%s=%s", parameters[0].getName(),
|
||||
parameters[0].getValue()));
|
||||
if (parameters.length > 1) {
|
||||
for (int i = 1; i < parameters.length; i++) {
|
||||
sb.append(parameters[i].toGetPara());
|
||||
}
|
||||
}
|
||||
}
|
||||
return doRequest(new HttpGet(sb.toString()));
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.foxinmy.weixin4j.model;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.alibaba.fastjson.annotation.JSONField;
|
||||
import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
|
||||
/**
|
||||
* access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token,正常情况下access_token有效期为7200秒,
|
||||
@@ -15,19 +14,27 @@ import com.thoughtworks.xstream.annotations.XStreamAlias;
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html">微信公众平台获取token</a>
|
||||
* @see <a href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8">微信企业号的主动模式</a>
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8">微信企业号的主动模式</a>
|
||||
*/
|
||||
@XStreamAlias("app-token")
|
||||
public class Token implements Serializable {
|
||||
|
||||
|
||||
private static final long serialVersionUID = -7564855472419104084L;
|
||||
|
||||
|
||||
@JSONField(name = "access_token")
|
||||
private String accessToken;
|
||||
@JSONField(name = "expires_in")
|
||||
private int expiresIn;
|
||||
private long time;
|
||||
|
||||
public Token() {
|
||||
|
||||
}
|
||||
|
||||
public Token(String accessToken) {
|
||||
this.accessToken = accessToken;
|
||||
}
|
||||
|
||||
public String getAccessToken() {
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.type.AccountType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
|
||||
/**
|
||||
* 获取weixin.properties中的id&secret信息
|
||||
*
|
||||
* @className AbstractTokenHolder
|
||||
* @author jy
|
||||
* @date 2014年10月6日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public abstract class AbstractTokenHolder implements TokenHolder {
|
||||
|
||||
protected final HttpRequest request = new HttpRequest();
|
||||
protected final WeixinAccount weixinAccount;
|
||||
|
||||
public AbstractTokenHolder(AccountType accountType) {
|
||||
this.weixinAccount = ConfigUtil
|
||||
.getWeixinAccount(accountType.getClazz());
|
||||
}
|
||||
|
||||
public AbstractTokenHolder(WeixinAccount weixinAccount) {
|
||||
this.weixinAccount = weixinAccount;
|
||||
}
|
||||
|
||||
public WeixinAccount getAccount() {
|
||||
return weixinAccount;
|
||||
}
|
||||
}
|
||||
@@ -1,87 +1,67 @@
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.Response;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.type.AccountType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.foxinmy.weixin4j.xml.XmlStream;
|
||||
|
||||
/**
|
||||
* 基于文件保存的Token获取类
|
||||
*
|
||||
* @className FileTokenHolder
|
||||
* @author jy.hu
|
||||
* @date 2014年9月27日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html">微信公众平台获取token说明</a>
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8">微信企业号获取token说明</a>
|
||||
* @see com.foxinmy.weixin4j.model.Token
|
||||
*/
|
||||
public class FileTokenHolder extends AbstractTokenHolder {
|
||||
|
||||
public FileTokenHolder(AccountType accountType) {
|
||||
super(accountType);
|
||||
}
|
||||
|
||||
public FileTokenHolder(WeixinAccount weixinAccount) {
|
||||
super(weixinAccount);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取token
|
||||
* <p>
|
||||
* 正常情况下返回{"access_token":"ACCESS_TOKEN","expires_in":7200},否则抛出异常.
|
||||
* </p>
|
||||
*
|
||||
* @return token对象
|
||||
* @throws WeixinException
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/index.php?title=%E8%8E%B7%E5%8F%96access_token">获取token说明</a>
|
||||
* @see com.foxinmy.weixin4j.model.Token
|
||||
*/
|
||||
@Override
|
||||
public Token getToken() throws WeixinException {
|
||||
String id = weixinAccount.getId();
|
||||
if (StringUtils.isBlank(id)
|
||||
|| StringUtils.isBlank(weixinAccount.getSecret())) {
|
||||
throw new IllegalArgumentException("id or secret not be null!");
|
||||
}
|
||||
File token_file = new File(String.format("%s/token_%s.xml",
|
||||
ConfigUtil.getValue("token_path"), id));
|
||||
Token token = null;
|
||||
Calendar ca = Calendar.getInstance();
|
||||
long now_time = ca.getTimeInMillis();
|
||||
try {
|
||||
if (token_file.exists()) {
|
||||
token = XmlStream.get(new FileInputStream(token_file),
|
||||
Token.class);
|
||||
long expire_time = token.getTime()
|
||||
+ (token.getExpiresIn() * 1000) - 2;
|
||||
if (expire_time > now_time) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
Response response = request.get(weixinAccount.getTokenUrl());
|
||||
token = response.getAsObject(new TypeReference<Token>() {
|
||||
});
|
||||
token.setTime(now_time);
|
||||
XmlStream.to(token, new FileOutputStream(token_file));
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Calendar;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
import com.thoughtworks.xstream.XStream;
|
||||
|
||||
/**
|
||||
* 用FILE保存TOKEN
|
||||
*
|
||||
* @className FileTokenHolder
|
||||
* @author jy
|
||||
* @date 2015年1月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.token.TokenCreator
|
||||
* @see com.foxinmy.weixin4j.token.WeixinTokenCreator
|
||||
*/
|
||||
public class FileTokenHolder implements TokenHolder {
|
||||
private final XStream xstream;
|
||||
private final String tokenPath;
|
||||
private final TokenCreator tokenCretor;
|
||||
|
||||
public FileTokenHolder(TokenCreator tokenCretor) {
|
||||
this(ConfigUtil.getValue("token_path"), tokenCretor);
|
||||
}
|
||||
|
||||
public FileTokenHolder(String tokenPath, TokenCreator tokenCretor) {
|
||||
this.tokenPath = tokenPath;
|
||||
this.tokenCretor = tokenCretor;
|
||||
xstream = new XStream();
|
||||
xstream.ignoreUnknownElements();
|
||||
xstream.autodetectAnnotations(true);
|
||||
xstream.alias("xml", Token.class);
|
||||
xstream.processAnnotations(Token.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token getToken() throws WeixinException {
|
||||
File token_file = new File(String.format("%s/%s.xml", tokenPath,
|
||||
tokenCretor.getCacheKey()));
|
||||
Token token = null;
|
||||
Calendar ca = Calendar.getInstance();
|
||||
long now_time = ca.getTimeInMillis();
|
||||
try {
|
||||
if (token_file.exists()) {
|
||||
token = (Token) xstream
|
||||
.fromXML(new FileInputStream(token_file));
|
||||
long expire_time = token.getTime()
|
||||
+ (token.getExpiresIn() * 1000) - 2;
|
||||
if (expire_time > now_time) {
|
||||
return token;
|
||||
}
|
||||
}
|
||||
token = tokenCretor.createToken();
|
||||
xstream.toXML(token, new FileOutputStream(token_file));
|
||||
} catch (IOException e) {
|
||||
throw new WeixinException(e.getMessage());
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,96 +1,81 @@
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.type.AccountType;
|
||||
|
||||
/**
|
||||
* 基于redis保存的Token获取类
|
||||
*
|
||||
* @className RedisTokenHolder
|
||||
* @author jy.hu
|
||||
* @date 2014年9月27日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html">微信公众平台获取token说明</a>
|
||||
* @see <a href=
|
||||
* "http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8"
|
||||
* >微信企业号获取token说明</a>
|
||||
* @see com.foxinmy.weixin4j.model.Token
|
||||
*/
|
||||
public class RedisTokenHolder extends AbstractTokenHolder {
|
||||
|
||||
private JedisPool jedisPool;
|
||||
|
||||
private void createPool(String host, int port) {
|
||||
JedisPoolConfig poolConfig = new JedisPoolConfig();
|
||||
poolConfig.setMaxTotal(50);
|
||||
poolConfig.setMaxIdle(5);
|
||||
poolConfig.setMaxWaitMillis(2000);
|
||||
poolConfig.setTestOnBorrow(false);
|
||||
poolConfig.setTestOnReturn(true);
|
||||
this.jedisPool = new JedisPool(poolConfig, host, port);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(String host, int port, AccountType accountType) {
|
||||
super(accountType);
|
||||
createPool(host, port);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(AccountType accountType) {
|
||||
this("localhost", 6379, accountType);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(WeixinAccount weixinAccount) {
|
||||
this("localhost", 6379, weixinAccount);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(String host, int port, WeixinAccount weixinAccount) {
|
||||
super(weixinAccount);
|
||||
createPool(host, port);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token getToken() throws WeixinException {
|
||||
String id = weixinAccount.getId();
|
||||
if (StringUtils.isBlank(id)
|
||||
|| StringUtils.isBlank(weixinAccount.getSecret())) {
|
||||
throw new IllegalArgumentException("id or secret not be null!");
|
||||
}
|
||||
Token token = null;
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
jedis = jedisPool.getResource();
|
||||
Calendar now = Calendar.getInstance();
|
||||
String key = String.format("token:%s", id);
|
||||
String accessToken = jedis.get(key);
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
token = request.get(weixinAccount.getTokenUrl()).getAsObject(
|
||||
new TypeReference<Token>() {
|
||||
});
|
||||
jedis.setex(key, token.getExpiresIn(),
|
||||
token.getAccessToken());
|
||||
token.setTime(now.getTimeInMillis());
|
||||
} else {
|
||||
token = new Token();
|
||||
token.setAccessToken(accessToken);
|
||||
}
|
||||
} catch (JedisException e) {
|
||||
jedisPool.returnBrokenResource(jedis);
|
||||
} finally {
|
||||
jedisPool.returnResource(jedis);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import redis.clients.jedis.Jedis;
|
||||
import redis.clients.jedis.JedisPool;
|
||||
import redis.clients.jedis.JedisPoolConfig;
|
||||
import redis.clients.jedis.exceptions.JedisException;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
|
||||
/**
|
||||
* 用REDIS保存TOKEN
|
||||
*
|
||||
* @className RedisTokenHolder
|
||||
* @author jy
|
||||
* @date 2015年1月9日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.token.TokenCreator
|
||||
* @see com.foxinmy.weixin4j.token.WeixinTokenCreator
|
||||
*/
|
||||
public class RedisTokenHolder implements TokenHolder {
|
||||
|
||||
private JedisPool jedisPool;
|
||||
private final TokenCreator tokenCretor;
|
||||
|
||||
public final static int MAX_TOTAL = 50;
|
||||
public final static int MAX_IDLE = 5;
|
||||
public final static int MAX_WAIT_MILLIS = 2000;
|
||||
public final static boolean TEST_ON_BORROW = false;
|
||||
public final static boolean TEST_ON_RETURN = true;
|
||||
|
||||
public RedisTokenHolder(TokenCreator tokenCretor) {
|
||||
this("localhost", 6379, tokenCretor);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(String host, int port, TokenCreator tokenCretor) {
|
||||
JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
|
||||
jedisPoolConfig.setMaxTotal(MAX_TOTAL);
|
||||
jedisPoolConfig.setMaxIdle(MAX_IDLE);
|
||||
jedisPoolConfig.setMaxWaitMillis(MAX_WAIT_MILLIS);
|
||||
jedisPoolConfig.setTestOnBorrow(TEST_ON_BORROW);
|
||||
jedisPoolConfig.setTestOnReturn(TEST_ON_RETURN);
|
||||
this.jedisPool = new JedisPool(jedisPoolConfig, host, port);
|
||||
this.tokenCretor = tokenCretor;
|
||||
}
|
||||
|
||||
public RedisTokenHolder(String host, int port,
|
||||
JedisPoolConfig jedisPoolConfig, TokenCreator tokenCretor) {
|
||||
this(new JedisPool(jedisPoolConfig, host, port), tokenCretor);
|
||||
}
|
||||
|
||||
public RedisTokenHolder(JedisPool jedisPool, TokenCreator tokenCretor) {
|
||||
this.jedisPool = jedisPool;
|
||||
this.tokenCretor = tokenCretor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token getToken() throws WeixinException {
|
||||
Token token = null;
|
||||
Jedis jedis = null;
|
||||
try {
|
||||
jedis = jedisPool.getResource();
|
||||
String cacheKey = tokenCretor.getCacheKey();
|
||||
String accessToken = jedis.get(cacheKey);
|
||||
if (StringUtils.isBlank(accessToken)) {
|
||||
token = tokenCretor.createToken();
|
||||
jedis.setex(cacheKey, (int) token.getExpiresIn(),
|
||||
token.getAccessToken());
|
||||
} else {
|
||||
token = new Token(accessToken);
|
||||
}
|
||||
} catch (JedisException e) {
|
||||
jedisPool.returnBrokenResource(jedis);
|
||||
} finally {
|
||||
jedisPool.returnResource(jedis);
|
||||
}
|
||||
return token;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
|
||||
/**
|
||||
* TOKEN创建者
|
||||
*
|
||||
* @className TokenCreator
|
||||
* @author jy
|
||||
* @date 2015年1月10日
|
||||
* @since JDK 1.7
|
||||
* @see
|
||||
*/
|
||||
public interface TokenCreator {
|
||||
/**
|
||||
* 返回缓存KEY的名称
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
public String getCacheKey();
|
||||
|
||||
/**
|
||||
* 创建token
|
||||
*
|
||||
* @return
|
||||
* @throws MeetException
|
||||
*/
|
||||
public Token createToken() throws WeixinException;
|
||||
}
|
||||
@@ -2,21 +2,18 @@ package com.foxinmy.weixin4j.token;
|
||||
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
|
||||
/**
|
||||
* 获取Token接口
|
||||
* token持有者
|
||||
*
|
||||
* @className TokenHolder
|
||||
* @author jy.hu
|
||||
* @date 2014年9月27日
|
||||
* @since JDK 1.7
|
||||
* @see com.foxinmy.weixin4j.model.Token
|
||||
* @see com.foxinmy.weixin4j.token.AbstractTokenHolder
|
||||
* @see com.foxinmy.weixin4j.token.FileTokenHolder
|
||||
* @see com.foxinmy.weixin4j.token.RedisTokenHolder
|
||||
*/
|
||||
public interface TokenHolder {
|
||||
public WeixinAccount getAccount();
|
||||
public Token getToken() throws WeixinException;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.foxinmy.weixin4j.token;
|
||||
|
||||
import com.alibaba.fastjson.TypeReference;
|
||||
import com.foxinmy.weixin4j.exception.WeixinException;
|
||||
import com.foxinmy.weixin4j.http.HttpRequest;
|
||||
import com.foxinmy.weixin4j.http.Response;
|
||||
import com.foxinmy.weixin4j.model.Token;
|
||||
import com.foxinmy.weixin4j.model.WeixinAccount;
|
||||
import com.foxinmy.weixin4j.type.AccountType;
|
||||
import com.foxinmy.weixin4j.util.ConfigUtil;
|
||||
|
||||
/**
|
||||
* 微信TOKEN创建者
|
||||
*
|
||||
* @className WeixinTokenCreator
|
||||
* @author jy
|
||||
* @date 2015年1月10日
|
||||
* @since JDK 1.7
|
||||
* @see <a
|
||||
* href="http://mp.weixin.qq.com/wiki/11/0e4b294685f817b95cbed85ba5e82b8f.html">微信公众平台获取token说明</a>
|
||||
* @see <a
|
||||
* href="http://qydev.weixin.qq.com/wiki/index.php?title=%E4%B8%BB%E5%8A%A8%E8%B0%83%E7%94%A8">微信企业号获取token说明</a>
|
||||
* @see com.foxinmy.weixin4j.model.Token
|
||||
*/
|
||||
public class WeixinTokenCreator implements TokenCreator {
|
||||
|
||||
private final HttpRequest request;
|
||||
private final WeixinAccount weixinAccount;
|
||||
|
||||
public WeixinTokenCreator(AccountType accountType) {
|
||||
this(ConfigUtil.getWeixinAccount(accountType.getClazz()));
|
||||
}
|
||||
|
||||
public WeixinTokenCreator(WeixinAccount weixinAccount) {
|
||||
this.request = new HttpRequest();
|
||||
this.weixinAccount = weixinAccount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCacheKey() {
|
||||
return weixinAccount.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Token createToken() throws WeixinException {
|
||||
Response response = request.get(weixinAccount.getTokenUrl());
|
||||
Token token = response.getAsObject(new TypeReference<Token>() {
|
||||
});
|
||||
token.setTime(System.currentTimeMillis());
|
||||
return token;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user