| @@ -9,12 +9,20 @@ import me.chanjar.weixin.common.bean.WxAccessToken; | |||||
| */ | */ | ||||
| public interface WxCpConfigStorage { | public interface WxCpConfigStorage { | ||||
| public String getAccessToken(); | |||||
| public boolean isAccessTokenExpired(); | |||||
| /** | |||||
| * 强制将access token过期掉 | |||||
| */ | |||||
| public void expireAccessToken(); | |||||
| public void updateAccessToken(WxAccessToken accessToken); | public void updateAccessToken(WxAccessToken accessToken); | ||||
| public void updateAccessToken(String accessToken, int expiresIn); | public void updateAccessToken(String accessToken, int expiresIn); | ||||
| public String getAccessToken(); | |||||
| public String getCorpId(); | public String getCorpId(); | ||||
| public String getCorpSecret(); | public String getCorpSecret(); | ||||
| @@ -25,7 +33,7 @@ public interface WxCpConfigStorage { | |||||
| public String getAesKey(); | public String getAesKey(); | ||||
| public int getExpiresIn(); | |||||
| public long getExpiresTime(); | |||||
| public String getOauth2redirectUri(); | public String getOauth2redirectUri(); | ||||
| @@ -16,7 +16,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| protected String accessToken; | protected String accessToken; | ||||
| protected String aesKey; | protected String aesKey; | ||||
| protected String agentId; | protected String agentId; | ||||
| protected int expiresIn; | |||||
| protected long expiresTime; | |||||
| protected String oauth2redirectUri; | protected String oauth2redirectUri; | ||||
| @@ -25,17 +25,25 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| protected String http_proxy_username; | protected String http_proxy_username; | ||||
| protected String http_proxy_password; | protected String http_proxy_password; | ||||
| public String getAccessToken() { | |||||
| return this.accessToken; | |||||
| } | |||||
| public boolean isAccessTokenExpired() { | |||||
| return System.currentTimeMillis() > this.expiresTime; | |||||
| } | |||||
| public void expireAccessToken() { | |||||
| this.expiresTime = 0; | |||||
| } | |||||
| public void updateAccessToken(WxAccessToken accessToken) { | public void updateAccessToken(WxAccessToken accessToken) { | ||||
| updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | ||||
| } | } | ||||
| public void updateAccessToken(String accessToken, int expiresIn) { | |||||
| public void updateAccessToken(String accessToken, int expiresInSeconds) { | |||||
| this.accessToken = accessToken; | this.accessToken = accessToken; | ||||
| this.expiresIn = expiresIn; | |||||
| } | |||||
| public String getAccessToken() { | |||||
| return this.accessToken; | |||||
| this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l; | |||||
| } | } | ||||
| public String getCorpId() { | public String getCorpId() { | ||||
| @@ -50,8 +58,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| return this.token; | return this.token; | ||||
| } | } | ||||
| public int getExpiresIn() { | |||||
| return this.expiresIn; | |||||
| public long getExpiresTime() { | |||||
| return this.expiresTime; | |||||
| } | } | ||||
| public void setCorpId(String corpId) { | public void setCorpId(String corpId) { | ||||
| @@ -78,8 +86,8 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| this.accessToken = accessToken; | this.accessToken = accessToken; | ||||
| } | } | ||||
| public void setExpiresIn(int expiresIn) { | |||||
| this.expiresIn = expiresIn; | |||||
| public void setExpiresTime(long expiresTime) { | |||||
| this.expiresTime = expiresTime; | |||||
| } | } | ||||
| public String getAgentId() { | public String getAgentId() { | ||||
| @@ -140,7 +148,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage { | |||||
| ", accessToken='" + accessToken + '\'' + | ", accessToken='" + accessToken + '\'' + | ||||
| ", aesKey='" + aesKey + '\'' + | ", aesKey='" + aesKey + '\'' + | ||||
| ", agentId='" + agentId + '\'' + | ", agentId='" + agentId + '\'' + | ||||
| ", expiresIn=" + expiresIn + | |||||
| ", expiresTime=" + expiresTime + | |||||
| ", http_proxy_host='" + http_proxy_host + '\'' + | ", http_proxy_host='" + http_proxy_host + '\'' + | ||||
| ", http_proxy_port=" + http_proxy_port + | ", http_proxy_port=" + http_proxy_port + | ||||
| ", http_proxy_username='" + http_proxy_username + '\'' + | ", http_proxy_username='" + http_proxy_username + '\'' + | ||||
| @@ -52,9 +52,10 @@ public interface WxCpService { | |||||
| * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | * 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token | ||||
| * </pre> | * </pre> | ||||
| * | * | ||||
| * @return | |||||
| * @throws me.chanjar.weixin.common.exception.WxErrorException | * @throws me.chanjar.weixin.common.exception.WxErrorException | ||||
| */ | */ | ||||
| public void accessTokenRefresh() throws WxErrorException; | |||||
| public String getAccessToken() throws WxErrorException; | |||||
| /** | /** | ||||
| * <pre> | * <pre> | ||||
| @@ -46,11 +46,9 @@ import java.util.concurrent.atomic.AtomicBoolean; | |||||
| public class WxCpServiceImpl implements WxCpService { | public class WxCpServiceImpl implements WxCpService { | ||||
| /** | /** | ||||
| * 全局的是否正在刷新Access Token的flag | |||||
| * true: 正在刷新 | |||||
| * false: 没有刷新 | |||||
| * 全局的是否正在刷新access token的锁 | |||||
| */ | */ | ||||
| protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false); | |||||
| protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object(); | |||||
| protected WxCpConfigStorage wxCpConfigStorage; | protected WxCpConfigStorage wxCpConfigStorage; | ||||
| @@ -73,45 +71,37 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| execute(new SimpleGetRequestExecutor(), url, null); | execute(new SimpleGetRequestExecutor(), url, null); | ||||
| } | } | ||||
| public void accessTokenRefresh() throws WxErrorException { | |||||
| if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) { | |||||
| try { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
| + "&corpid=" + wxCpConfigStorage.getCorpId() | |||||
| + "&corpsecret=" + wxCpConfigStorage.getCorpSecret(); | |||||
| try { | |||||
| HttpGet httpGet = new HttpGet(url); | |||||
| if (httpProxy != null) { | |||||
| RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); | |||||
| httpGet.setConfig(config); | |||||
| } | |||||
| CloseableHttpClient httpclient = getHttpclient(); | |||||
| CloseableHttpResponse response = httpclient.execute(httpGet); | |||||
| String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
| WxError error = WxError.fromJson(resultContent); | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error); | |||||
| public String getAccessToken() throws WxErrorException { | |||||
| if (wxCpConfigStorage.isAccessTokenExpired()) { | |||||
| synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) { | |||||
| if (wxCpConfigStorage.isAccessTokenExpired()) { | |||||
| String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?" | |||||
| + "&corpid=" + wxCpConfigStorage.getCorpId() | |||||
| + "&corpsecret=" + wxCpConfigStorage.getCorpSecret(); | |||||
| try { | |||||
| HttpGet httpGet = new HttpGet(url); | |||||
| if (httpProxy != null) { | |||||
| RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build(); | |||||
| httpGet.setConfig(config); | |||||
| } | |||||
| CloseableHttpClient httpclient = getHttpclient(); | |||||
| CloseableHttpResponse response = httpclient.execute(httpGet); | |||||
| String resultContent = new BasicResponseHandler().handleResponse(response); | |||||
| WxError error = WxError.fromJson(resultContent); | |||||
| if (error.getErrorCode() != 0) { | |||||
| throw new WxErrorException(error); | |||||
| } | |||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
| wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | } | ||||
| WxAccessToken accessToken = WxAccessToken.fromJson(resultContent); | |||||
| wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn()); | |||||
| } catch (ClientProtocolException e) { | |||||
| throw new RuntimeException(e); | |||||
| } catch (IOException e) { | |||||
| throw new RuntimeException(e); | |||||
| } | } | ||||
| } finally { | |||||
| GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false); | |||||
| } | } | ||||
| } else { | |||||
| // 每隔100ms检查一下是否刷新完毕了 | |||||
| while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) { | |||||
| try { | |||||
| Thread.sleep(100); | |||||
| } catch (InterruptedException e) { | |||||
| } | |||||
| } | |||||
| // 刷新完毕了,就没他什么事儿了 | |||||
| } | } | ||||
| return wxCpConfigStorage.getAccessToken(); | |||||
| } | } | ||||
| public void messageSend(WxCpMessage message) throws WxErrorException { | public void messageSend(WxCpMessage message) throws WxErrorException { | ||||
| @@ -369,10 +359,7 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| * @throws WxErrorException | * @throws WxErrorException | ||||
| */ | */ | ||||
| public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException { | ||||
| if (StringUtils.isBlank(wxCpConfigStorage.getAccessToken())) { | |||||
| accessTokenRefresh(); | |||||
| } | |||||
| String accessToken = wxCpConfigStorage.getAccessToken(); | |||||
| String accessToken = getAccessToken(); | |||||
| String uriWithAccessToken = uri; | String uriWithAccessToken = uri; | ||||
| uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; | uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken; | ||||
| @@ -387,7 +374,8 @@ public class WxCpServiceImpl implements WxCpService { | |||||
| * 42001 access_token超时 | * 42001 access_token超时 | ||||
| */ | */ | ||||
| if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) { | ||||
| accessTokenRefresh(); | |||||
| // 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token | |||||
| wxCpConfigStorage.expireAccessToken(); | |||||
| return execute(executor, uri, data); | return execute(executor, uri, data); | ||||
| } | } | ||||
| /** | /** | ||||
| @@ -22,7 +22,7 @@ public class WxCpBaseAPITest { | |||||
| public void testRefreshAccessToken() throws WxErrorException { | public void testRefreshAccessToken() throws WxErrorException { | ||||
| WxCpConfigStorage configStorage = wxService.wxCpConfigStorage; | WxCpConfigStorage configStorage = wxService.wxCpConfigStorage; | ||||
| String before = configStorage.getAccessToken(); | String before = configStorage.getAccessToken(); | ||||
| wxService.accessTokenRefresh(); | |||||
| wxService.getAccessToken(); | |||||
| String after = configStorage.getAccessToken(); | String after = configStorage.getAccessToken(); | ||||
| @@ -16,7 +16,7 @@ class WxCpDemoInMemoryConfigStorage extends WxCpInMemoryConfigStorage { | |||||
| @Override | @Override | ||||
| public String toString() { | public String toString() { | ||||
| return "SimpleWxConfigProvider [appidOrCorpid=" + corpId + ", corpSecret=" + corpSecret + ", accessToken=" + accessToken | return "SimpleWxConfigProvider [appidOrCorpid=" + corpId + ", corpSecret=" + corpSecret + ", accessToken=" + accessToken | ||||
| + ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]"; | |||||
| + ", expiresTime=" + expiresTime + ", token=" + token + ", aesKey=" + aesKey + "]"; | |||||
| } | } | ||||