Skip to content

Commit f3cdf22

Browse files
committed
Merge branch 'feature/jsapi' into develop
2 parents f00c549 + 4f292da commit f3cdf22

File tree

19 files changed

+347
-145
lines changed

19 files changed

+347
-145
lines changed

weixin-java-common/src/main/java/me/chanjar/weixin/common/util/crypto/SHA1.java

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,27 +10,49 @@
1010
public class SHA1 {
1111

1212
/**
13-
* 生成SHA1签名
13+
* 串接arr参数,生成sha1 digest
14+
*
1415
* @param arr
1516
* @return
1617
*/
1718
public static String gen(String... arr) throws NoSuchAlgorithmException {
1819
Arrays.sort(arr);
1920
StringBuilder sb = new StringBuilder();
20-
for(String a : arr) {
21+
for (String a : arr) {
2122
sb.append(a);
2223
}
24+
return genStr(sb.toString());
25+
}
26+
27+
/**
28+
* 用&串接arr参数,生成sha1 digest
29+
*
30+
* @param arr
31+
* @return
32+
*/
33+
public static String genWithAmple(String... arr) throws NoSuchAlgorithmException {
34+
Arrays.sort(arr);
35+
StringBuilder sb = new StringBuilder();
36+
for (int i = 0; i < arr.length; i++) {
37+
String a = arr[i];
38+
sb.append(a);
39+
if (i != arr.length - 1) {
40+
sb.append('&');
41+
}
42+
}
43+
return genStr(sb.toString());
44+
}
2345

46+
public static String genStr(String str) throws NoSuchAlgorithmException {
2447
MessageDigest sha1 = MessageDigest.getInstance("SHA1");
25-
sha1.update(sb.toString().getBytes());
48+
sha1.update(str.getBytes());
2649
byte[] output = sha1.digest();
2750
return bytesToHex(output);
2851
}
2952

30-
3153
protected static String bytesToHex(byte[] b) {
32-
char hexDigit[] = {'0', '1', '2', '3', '4', '5', '6', '7',
33-
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
54+
char hexDigit[] = { '0', '1', '2', '3', '4', '5', '6', '7',
55+
'8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
3456
StringBuffer buf = new StringBuffer();
3557
for (int j = 0; j < b.length; j++) {
3658
buf.append(hexDigit[(b[j] >> 4) & 0x0f]);

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpConfigStorage.java

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,20 @@
99
*/
1010
public interface WxCpConfigStorage {
1111

12+
public String getAccessToken();
13+
14+
public boolean isAccessTokenExpired();
15+
16+
/**
17+
* 强制将access token过期掉
18+
*/
19+
public void expireAccessToken();
20+
1221
public void updateAccessToken(WxAccessToken accessToken);
13-
22+
1423
public void updateAccessToken(String accessToken, int expiresIn);
15-
16-
public String getAccessToken();
17-
24+
25+
1826
public String getCorpId();
1927

2028
public String getCorpSecret();
@@ -25,7 +33,7 @@ public interface WxCpConfigStorage {
2533

2634
public String getAesKey();
2735

28-
public int getExpiresIn();
36+
public long getExpiresTime();
2937

3038
public String getOauth2redirectUri();
3139

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpInMemoryConfigStorage.java

Lines changed: 20 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
1616
protected String accessToken;
1717
protected String aesKey;
1818
protected String agentId;
19-
protected int expiresIn;
19+
protected long expiresTime;
2020

2121
protected String oauth2redirectUri;
2222

@@ -25,17 +25,25 @@ public class WxCpInMemoryConfigStorage implements WxCpConfigStorage {
2525
protected String http_proxy_username;
2626
protected String http_proxy_password;
2727

28+
public String getAccessToken() {
29+
return this.accessToken;
30+
}
31+
32+
public boolean isAccessTokenExpired() {
33+
return System.currentTimeMillis() > this.expiresTime;
34+
}
35+
36+
public void expireAccessToken() {
37+
this.expiresTime = 0;
38+
}
39+
2840
public void updateAccessToken(WxAccessToken accessToken) {
2941
updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
3042
}
3143

32-
public void updateAccessToken(String accessToken, int expiresIn) {
44+
public void updateAccessToken(String accessToken, int expiresInSeconds) {
3345
this.accessToken = accessToken;
34-
this.expiresIn = expiresIn;
35-
}
36-
37-
public String getAccessToken() {
38-
return this.accessToken;
46+
this.expiresTime = System.currentTimeMillis() + (expiresInSeconds - 200) * 1000l;
3947
}
4048

4149
public String getCorpId() {
@@ -50,8 +58,8 @@ public String getToken() {
5058
return this.token;
5159
}
5260

53-
public int getExpiresIn() {
54-
return this.expiresIn;
61+
public long getExpiresTime() {
62+
return this.expiresTime;
5563
}
5664

5765
public void setCorpId(String corpId) {
@@ -78,8 +86,8 @@ public void setAccessToken(String accessToken) {
7886
this.accessToken = accessToken;
7987
}
8088

81-
public void setExpiresIn(int expiresIn) {
82-
this.expiresIn = expiresIn;
89+
public void setExpiresTime(long expiresTime) {
90+
this.expiresTime = expiresTime;
8391
}
8492

8593
public String getAgentId() {
@@ -140,7 +148,7 @@ public String toString() {
140148
", accessToken='" + accessToken + '\'' +
141149
", aesKey='" + aesKey + '\'' +
142150
", agentId='" + agentId + '\'' +
143-
", expiresIn=" + expiresIn +
151+
", expiresTime=" + expiresTime +
144152
", http_proxy_host='" + http_proxy_host + '\'' +
145153
", http_proxy_port=" + http_proxy_port +
146154
", http_proxy_username='" + http_proxy_username + '\'' +

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpService.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,11 @@ public interface WxCpService {
5151
* 程序员在非必要情况下尽量不要主动调用此方法
5252
* 详情请见: http://mp.weixin.qq.com/wiki/index.php?title=获取access_token
5353
* </pre>
54-
*
54+
* @param forceRefresh 强制刷新
55+
* @return
5556
* @throws me.chanjar.weixin.common.exception.WxErrorException
5657
*/
57-
public void accessTokenRefresh() throws WxErrorException;
58+
public String getAccessToken(boolean forceRefresh) throws WxErrorException;
5859

5960
/**
6061
* <pre>

weixin-java-cp/src/main/java/me/chanjar/weixin/cp/api/WxCpServiceImpl.java

Lines changed: 35 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -46,11 +46,9 @@
4646
public class WxCpServiceImpl implements WxCpService {
4747

4848
/**
49-
* 全局的是否正在刷新Access Token的flag
50-
* true: 正在刷新
51-
* false: 没有刷新
49+
* 全局的是否正在刷新access token的锁
5250
*/
53-
protected static final AtomicBoolean GLOBAL_ACCESS_TOKEN_REFRESH_FLAG = new AtomicBoolean(false);
51+
protected static final Object GLOBAL_ACCESS_TOKEN_REFRESH_LOCK = new Object();
5452

5553
protected WxCpConfigStorage wxCpConfigStorage;
5654

@@ -73,45 +71,40 @@ public void userAuthenticated(String userId) throws WxErrorException {
7371
execute(new SimpleGetRequestExecutor(), url, null);
7472
}
7573

76-
public void accessTokenRefresh() throws WxErrorException {
77-
if (!GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.getAndSet(true)) {
78-
try {
79-
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
80-
+ "&corpid=" + wxCpConfigStorage.getCorpId()
81-
+ "&corpsecret=" + wxCpConfigStorage.getCorpSecret();
82-
try {
83-
HttpGet httpGet = new HttpGet(url);
84-
if (httpProxy != null) {
85-
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
86-
httpGet.setConfig(config);
87-
}
88-
CloseableHttpClient httpclient = getHttpclient();
89-
CloseableHttpResponse response = httpclient.execute(httpGet);
90-
String resultContent = new BasicResponseHandler().handleResponse(response);
91-
WxError error = WxError.fromJson(resultContent);
92-
if (error.getErrorCode() != 0) {
93-
throw new WxErrorException(error);
74+
public String getAccessToken(boolean forceRefresh) throws WxErrorException {
75+
if (forceRefresh) {
76+
wxCpConfigStorage.expireAccessToken();
77+
}
78+
if (wxCpConfigStorage.isAccessTokenExpired()) {
79+
synchronized (GLOBAL_ACCESS_TOKEN_REFRESH_LOCK) {
80+
if (wxCpConfigStorage.isAccessTokenExpired()) {
81+
String url = "https://qyapi.weixin.qq.com/cgi-bin/gettoken?"
82+
+ "&corpid=" + wxCpConfigStorage.getCorpId()
83+
+ "&corpsecret=" + wxCpConfigStorage.getCorpSecret();
84+
try {
85+
HttpGet httpGet = new HttpGet(url);
86+
if (httpProxy != null) {
87+
RequestConfig config = RequestConfig.custom().setProxy(httpProxy).build();
88+
httpGet.setConfig(config);
89+
}
90+
CloseableHttpClient httpclient = getHttpclient();
91+
CloseableHttpResponse response = httpclient.execute(httpGet);
92+
String resultContent = new BasicResponseHandler().handleResponse(response);
93+
WxError error = WxError.fromJson(resultContent);
94+
if (error.getErrorCode() != 0) {
95+
throw new WxErrorException(error);
96+
}
97+
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
98+
wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
99+
} catch (ClientProtocolException e) {
100+
throw new RuntimeException(e);
101+
} catch (IOException e) {
102+
throw new RuntimeException(e);
94103
}
95-
WxAccessToken accessToken = WxAccessToken.fromJson(resultContent);
96-
wxCpConfigStorage.updateAccessToken(accessToken.getAccessToken(), accessToken.getExpiresIn());
97-
} catch (ClientProtocolException e) {
98-
throw new RuntimeException(e);
99-
} catch (IOException e) {
100-
throw new RuntimeException(e);
101-
}
102-
} finally {
103-
GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.set(false);
104-
}
105-
} else {
106-
// 每隔100ms检查一下是否刷新完毕了
107-
while (GLOBAL_ACCESS_TOKEN_REFRESH_FLAG.get()) {
108-
try {
109-
Thread.sleep(100);
110-
} catch (InterruptedException e) {
111104
}
112105
}
113-
// 刷新完毕了,就没他什么事儿了
114106
}
107+
return wxCpConfigStorage.getAccessToken();
115108
}
116109

117110
public void messageSend(WxCpMessage message) throws WxErrorException {
@@ -369,10 +362,7 @@ public String post(String url, String postData) throws WxErrorException {
369362
* @throws WxErrorException
370363
*/
371364
public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) throws WxErrorException {
372-
if (StringUtils.isBlank(wxCpConfigStorage.getAccessToken())) {
373-
accessTokenRefresh();
374-
}
375-
String accessToken = wxCpConfigStorage.getAccessToken();
365+
String accessToken = getAccessToken(false);
376366

377367
String uriWithAccessToken = uri;
378368
uriWithAccessToken += uri.indexOf('?') == -1 ? "?access_token=" + accessToken : "&access_token=" + accessToken;
@@ -387,7 +377,8 @@ public <T, E> T execute(RequestExecutor<T, E> executor, String uri, E data) thro
387377
* 42001 access_token超时
388378
*/
389379
if (error.getErrorCode() == 42001 || error.getErrorCode() == 40001) {
390-
accessTokenRefresh();
380+
// 强制设置wxCpConfigStorage它的access token过期了,这样在下一次请求里就会刷新access token
381+
wxCpConfigStorage.expireAccessToken();
391382
return execute(executor, uri, data);
392383
}
393384
/**

weixin-java-cp/src/test/java/me/chanjar/weixin/cp/api/WxCpBaseAPITest.java

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

2727
String after = configStorage.getAccessToken();
2828

weixin-java-cp/src/test/java/me/chanjar/weixin/cp/demo/WxCpDemoInMemoryConfigStorage.java

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

2222

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: 39 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,21 +9,52 @@
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+
21+
/**
22+
* 应该是线程安全的
23+
* @param accessToken
24+
*/
1225
public void updateAccessToken(WxAccessToken accessToken);
13-
26+
27+
/**
28+
* 应该是线程安全的
29+
* @param accessToken
30+
* @param expiresIn
31+
*/
1432
public void updateAccessToken(String accessToken, int expiresIn);
15-
16-
public String getAccessToken();
17-
33+
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+
1849
public String getAppId();
19-
50+
2051
public String getSecret();
21-
52+
2253
public String getToken();
2354

2455
public String getAesKey();
2556

26-
public int getExpiresIn();
57+
public long getExpiresTime();
2758

2859
public String getOauth2redirectUri();
2960

@@ -33,6 +64,7 @@ public interface WxMpConfigStorage {
3364

3465
public String getHttp_proxy_username();
3566

67+
3668
public String getHttp_proxy_password();
3769

3870
}

0 commit comments

Comments
 (0)