Skip to content

Commit 1bca854

Browse files
committed
refactor: mp 调整access token刷新策略
1 parent cef8ac0 commit 1bca854

File tree

10 files changed

+115
-96
lines changed

10 files changed

+115
-96
lines changed

weixin-java-cp/src/test/resources/test-config.sample.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<token>企业号应用Token</token>
66
<aesKey>企业号应用EncodingAESKey</aesKey>
77
<accessToken>可以不填写</accessToken>
8-
<expiresIn>可以不填写</expiresIn>
8+
<expiresTime>可以不填写</expiresTime>
99
<userId>企业号通讯录里的某个userid</userId>
1010
<departmentId>企业号通讯录的某个部门id</departmentId>
1111
<tagId>企业号通讯录里的某个tagid</tagId>

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpConfigStorage.java

Lines changed: 28 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,15 @@
99
*/
1010
public interface WxMpConfigStorage {
1111

12+
public String getAccessToken();
13+
14+
public boolean isAccessTokenExpired();
15+
16+
/**
17+
* 强制将access token过期掉
18+
*/
19+
public void expireAccessToken();
20+
1221
/**
1322
* 应该是线程安全的
1423
* @param accessToken
@@ -22,17 +31,30 @@ public interface WxMpConfigStorage {
2231
*/
2332
public void updateAccessToken(String accessToken, int expiresIn);
2433

25-
public String getAccessToken();
26-
34+
public String getJsapiTicket();
35+
36+
public boolean isJsapiTicketExpired();
37+
38+
/**
39+
* 强制将jsapi ticket过期掉
40+
*/
41+
public void expireJsapiTicket();
42+
43+
/**
44+
* 应该是线程安全的
45+
* @param jsapiTicket
46+
*/
47+
public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
48+
2749
public String getAppId();
28-
50+
2951
public String getSecret();
30-
52+
3153
public String getToken();
3254

3355
public String getAesKey();
3456

35-
public int getExpiresIn();
57+
public long getExpiresTime();
3658

3759
public String getOauth2redirectUri();
3860

@@ -42,17 +64,7 @@ public interface WxMpConfigStorage {
4264

4365
public String getHttp_proxy_username();
4466

45-
public String getHttp_proxy_password();
46-
47-
48-
public String getJsapiTicket();
49-
50-
public boolean isJsapiTokenExpired();
5167

52-
/**
53-
* 应该是线程安全的
54-
* @param jsapiTicket
55-
*/
56-
public void updateJsapiTicket(String jsapiTicket, int expiresInSeconds);
68+
public String getHttp_proxy_password();
5769

5870
}

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpInMemoryConfigStorage.java

Lines changed: 36 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
1616
protected String token;
1717
protected String accessToken;
1818
protected String aesKey;
19-
protected int expiresIn;
19+
protected long expiresTime;
2020

2121
protected String oauth2redirectUri;
2222

@@ -28,17 +28,43 @@ public class WxMpInMemoryConfigStorage implements WxMpConfigStorage {
2828
protected String jsapiTicket;
2929
protected long jsapiTicketExpiresTime;
3030

31+
public String getAccessToken() {
32+
return this.accessToken;
33+
}
34+
35+
public boolean isAccessTokenExpired() {
36+
return System.currentTimeMillis() > this.expiresTime;
37+
}
38+
3139
public synchronized void updateAccessToken(WxAccessToken accessToken) {
3240
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
3341
}
3442

35-
public synchronized void updateAccessToken(String accessToken, int expiresIn) {
43+
public synchronized void updateAccessToken(String accessToken, int expiresInSeconds) {
3644
this.accessToken = accessToken;
37-
this.expiresIn = expiresIn;
45+
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
3846
}
3947

40-
public String getAccessToken() {
41-
return this.accessToken;
48+
public void expireAccessToken() {
49+
this.expiresTime = 0;
50+
}
51+
52+
public String getJsapiTicket() {
53+
return jsapiTicket;
54+
}
55+
56+
public boolean isJsapiTicketExpired() {
57+
return System.currentTimeMillis() > this.jsapiTicketExpiresTime;
58+
}
59+
60+
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
61+
this.jsapiTicket = jsapiTicket;
62+
// 预留200秒的时间
63+
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
64+
}
65+
66+
public void expireJsapiTicket() {
67+
this.jsapiTicketExpiresTime = 0;
4268
}
4369

4470
public String getAppId() {
@@ -53,8 +79,8 @@ public String getToken() {
5379
return this.token;
5480
}
5581

56-
public int getExpiresIn() {
57-
return this.expiresIn;
82+
public long getExpiresTime() {
83+
return this.expiresTime;
5884
}
5985

6086
public void setAppId(String appId) {
@@ -81,8 +107,8 @@ public void setAccessToken(String accessToken) {
81107
this.accessToken = accessToken;
82108
}
83109

84-
public void setExpiresIn(int expiresIn) {
85-
this.expiresIn = expiresIn;
110+
public void setExpiresTime(long expiresTime) {
111+
this.expiresTime = expiresTime;
86112
}
87113

88114
@Override
@@ -127,20 +153,6 @@ public void setHttp_proxy_password(String http_proxy_password) {
127153
}
128154

129155

130-
public String getJsapiTicket() {
131-
return jsapiTicket;
132-
}
133-
134-
public boolean isJsapiTokenExpired() {
135-
return System.currentTimeMillis() > this.jsapiTicketExpiresTime;
136-
}
137-
138-
public synchronized void updateJsapiTicket(String jsapiTicket, int expiresInSeconds) {
139-
this.jsapiTicket = jsapiTicket;
140-
// 预留200秒的时间
141-
this.jsapiTicketExpiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
142-
}
143-
144156
@Override
145157
public String toString() {
146158
return "WxMpInMemoryConfigStorage{" +
@@ -149,7 +161,7 @@ public String toString() {
149161
", token='" + token + '\'' +
150162
", accessToken='" + accessToken + '\'' +
151163
", aesKey='" + aesKey + '\'' +
152-
", expiresIn=" + expiresIn +
164+
", expiresTime=" + expiresTime +
153165
", http_proxy_host='" + http_proxy_host + '\'' +
154166
", http_proxy_port=" + http_proxy_port +
155167
", http_proxy_username='" + http_proxy_username + '\'' +

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpService.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,10 @@ public interface WxMpService {
4040
4141
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
4242
* </pre>
43+
* @return
4344
* @throws me.chanjar.weixin.common.exception.WxErrorException
4445
*/
45-
public void accessTokenRefresh() throws WxErrorException;
46+
public String getAccessToken() throws WxErrorException;
4647

4748
/**
4849
* <pre>

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/api/WxMpServiceImpl.java

Lines changed: 43 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -44,12 +44,15 @@
4444
public class WxMpServiceImpl implements WxMpService {
4545

4646
/**
47-
* 全局的是否正在刷新Access Token的flag
48-
* true: 正在刷新
49-
* false: 没有刷新
47+
* 全局的是否正在刷新access token的锁
5048
*/
51-
protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
52-
49+
protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object();
50+
51+
/**
52+
* 全局的是否正在刷新jsapi_ticket的锁
53+
*/
54+
protected static final Object GLOBAL_JSAPI_TICKET_REFRESH_LOCK = new Object();
55+
5356
protected WxMpConfigStorage wxMpConfigStorage;
5457

5558
protected final ThreadLocal<Integer> retryTimes = new ThreadLocal<Integer>();
@@ -66,52 +69,45 @@ public boolean checkSignature(String timestamp, String nonce, String signature)
6669
}
6770
}
6871

69-
public void accessTokenRefresh() throws WxErrorException {
70-
if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
71-
try {
72-
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
73-
+ "&appid=" + wxMpConfigStorage.getAppId()
74-
+ "&secret=" + wxMpConfigStorage.getSecret()
75-
;
76-
try {
77-
HttpGet httpGet = new HttpGet(url);
78-
if (httpProxy != null) {
79-
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
80-
httpGet.setConfig(config);
81-
}
82-
CloseableHttpClient httpclient = getHttpclient();
83-
CloseableHttpResponse response = httpclient.execute(httpGet);
84-
String resultContent = new BasicResponseHandler().handleResponse(response);
85-
WxError error = WxError.fromJson(resultContent);
86-
if (error.getErrorCode() != 0) {
87-
throw new WxErrorException(error);
72+
public String getAccessToken() throws WxErrorException {
73+
if (wxMpConfigStorage.isAccessTokenExpired()) {
74+
synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) {
75+
if (wxMpConfigStorage.isAccessTokenExpired()) {
76+
String url = "https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential"
77+
+ "&appid=" + wxMpConfigStorage.getAppId()
78+
+ "&secret=" + wxMpConfigStorage.getSecret()
79+
;
80+
try {
81+
HttpGet httpGet = new HttpGet(url);
82+
if (httpProxy != null) {
83+
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
84+
httpGet.setConfig(config);
85+
}
86+
CloseableHttpClient httpclient = getHttpclient();
87+
CloseableHttpResponse response = httpclient.execute(httpGet);
88+
String resultContent = new BasicResponseHandler().handleResponse(response);
89+
WxError error = WxError.fromJson(resultContent);
90+
if (error.getErrorCode() != 0) {
91+
throw new WxErrorException(error);
92+
}
93+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
94+
wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
95+
} catch (ClientProtocolException e) {
96+
throw new RuntimeException(e);
97+
} catch (IOException e) {
98+
throw new RuntimeException(e);
8899
}
89-
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
90-
wxMpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
91-
} catch (ClientProtocolException e) {
92-
throw new RuntimeException(e);
93-
} catch (IOException e) {
94-
throw new RuntimeException(e);
95100
}
96-
} finally {
97-
GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
98101
}
99-
} else {
100-
// 每隔100ms检查一下是否刷新完毕了
101-
while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
102-
try {
103-
Thread.sleep(100);
104-
} catch (InterruptedException e) {
105-
}
106-
}
107-
// 刷新完毕了,就没他什么事儿了
108102
}
103+
return wxMpConfigStorage.getAccessToken();
109104
}
110105

106+
111107
public String getJsapiTicket() throws WxErrorException {
112-
if (wxMpConfigStorage.isJsapiTokenExpired()) {
113-
synchronized (wxMpConfigStorage) {
114-
if (wxMpConfigStorage.isJsapiTokenExpired()) {
108+
if (wxMpConfigStorage.isJsapiTicketExpired()) {
109+
synchronized (GLOBAL_JSAPI_TICKET_REFRESH_LOCK) {
110+
if (wxMpConfigStorage.isJsapiTicketExpired()) {
115111
String url = "https://api.weixin.qq.com/cgi-bin/ticket/getticket?type=jsapi";
116112
String responseContent = execute(new SimpleGetRequestExecutor(), url, null);
117113
JsonElement tmpJsonElement = Streams.parse(new JsonReader(new StringReader(responseContent)));
@@ -440,10 +436,7 @@ public String post(String url, String postData) throws WxErrorException {
440436
* @throws WxErrorException
441437
*/
442438
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
443-
if (StringUtils.isBlank(wxMpConfigStorage.getAccessToken())) {
444-
accessTokenRefresh();
445-
}
446-
String accessToken = wxMpConfigStorage.getAccessToken();
439+
String accessToken = getAccessToken();
447440

448441
String uriWithAccessToken = uri;
449442
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
@@ -458,7 +451,8 @@ public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) thro
458451
* 42001 access_token超时
459452
*/
460453
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
461-
accessTokenRefresh();
454+
// 强制设置wxMpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
455+
wxMpConfigStorage.expireAccessToken();
462456
return execute(executor, uri, data);
463457
}
464458
/**

weixin-java-mp/src/main/java/me/chanjar/weixin/mp/bean/result/WxMpOAuth2AccessToken.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ public static WxMpOAuth2AccessToken fromJson(String json) {
6565
public String toString() {
6666
return "WxMpOAuth2AccessToken{" +
6767
"accessToken='" + accessToken + '\'' +
68-
", expiresIn=" + expiresIn +
68+
", expiresTime=" + expiresIn +
6969
", refreshToken='" + refreshToken + '\'' +
7070
", openId='" + openId + '\'' +
7171
", scope='" + scope + '\'' +

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/ApiTestModule.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ public void setOpenId(String openId) {
4242
@Override
4343
public String toString() {
4444
return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
45-
+ ", expiresIn=" + expiresIn + ", token=" + token + ", openId=" + openId + "]";
45+
+ ", expiresTime=" + expiresTime + ", token=" + token + ", openId=" + openId + "]";
4646
}
4747

4848
}

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/api/WxMpBaseAPITest.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ public class WxMpBaseAPITest {
2222
public void testRefreshAccessToken() throws WxErrorException {
2323
WxMpConfigStorage configStorage = wxService.wxMpConfigStorage;
2424
String before = configStorage.getAccessToken();
25-
wxService.accessTokenRefresh();
25+
wxService.getAccessToken();
2626

2727
String after = configStorage.getAccessToken();
2828

weixin-java-mp/src/test/java/me/chanjar/weixin/mp/demo/WxMpDemoInMemoryConfigStorage.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ class WxMpDemoInMemoryConfigStorage extends WxMpInMemoryConfigStorage {
1616
@Override
1717
public String toString() {
1818
return "SimpleWxConfigProvider [appId=" + appId + ", secret=" + secret + ", accessToken=" + accessToken
19-
+ ", expiresIn=" + expiresIn + ", token=" + token + ", aesKey=" + aesKey + "]";
19+
+ ", expiresTime=" + expiresTime + ", token=" + token + ", aesKey=" + aesKey + "]";
2020
}
2121

2222

weixin-java-mp/src/test/resources/test-config.sample.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
<token>公众号Token</token>
55
<aesKey>公众号EncodingAESKey</aesKey>
66
<accessToken>可以不填写</accessToken>
7-
<expiresIn>可以不填写</expiresIn>
7+
<expiresTime>可以不填写</expiresTime>
88
<openId>某个加你公众号的用户的openId</openId>
99
<oauth2redirectUri>网页授权获取用户信息回调地址</oauth2redirectUri>
1010
</xml>

0 commit comments

Comments
 (0)