BeisenTokenManager.java 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. package com.kingdee.eas.custom.beisen.utils;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.JSONObject;
  4. import okhttp3.*;
  5. import org.apache.log4j.Logger;
  6. import java.io.IOException;
  7. import java.util.Map;
  8. import java.util.concurrent.TimeUnit;
  9. import java.util.concurrent.locks.Lock;
  10. import java.util.concurrent.locks.ReentrantLock;
  11. public class BeisenTokenManager {
  12. private static final Logger logger = Logger.getLogger(BeisenTokenManager.class);
  13. private static final MediaType JSON_MEDIA_TYPE = MediaType.parse("application/json; charset=utf-8");
  14. private static final long EXPIRY_BUFFER = 300_000; // 5分钟缓冲时间
  15. private final OkHttpClient httpClient;
  16. private final Lock lock = new ReentrantLock();
  17. private volatile TokenState tokenState;
  18. private final String appKey;
  19. private final String appSecret;
  20. private final String tokenUrl;
  21. // 添加单例实例
  22. private static volatile BeisenTokenManager instance;
  23. // 私有化构造器
  24. private BeisenTokenManager() {
  25. this(Config.APP_KEY, Config.APP_SECRET, Config.TOKEN_URL);
  26. }
  27. // 获取单例方法
  28. public static BeisenTokenManager getInstance() {
  29. if (instance == null) {
  30. synchronized (BeisenTokenManager.class) {
  31. if (instance == null) {
  32. instance = new BeisenTokenManager();
  33. }
  34. }
  35. }
  36. return instance;
  37. }
  38. public BeisenTokenManager(String appKey, String appSecret, String tokenUrl) {
  39. this.appKey = appKey;
  40. this.appSecret = appSecret;
  41. this.tokenUrl = tokenUrl;
  42. this.httpClient = buildHttpClient();
  43. this.tokenState = loadInitialToken();
  44. }
  45. public String getAccessToken() throws IOException {
  46. TokenState currentState = tokenState;
  47. if (currentState != null && !isTokenExpired(currentState)) {
  48. return currentState.getAccessToken();
  49. }
  50. if (currentState == null || isTokenExpired(currentState)) {
  51. lock.lock();
  52. try {
  53. currentState = tokenState;
  54. if (currentState == null || isTokenExpired(currentState)) {
  55. currentState = fetchNewToken();
  56. tokenState = currentState;
  57. }
  58. } finally {
  59. lock.unlock();
  60. }
  61. }
  62. return currentState.getAccessToken();
  63. }
  64. public void refreshToken() throws IOException {
  65. TokenState currentState = tokenState;
  66. if (currentState == null || currentState.getRefreshToken() == null) {
  67. tokenState = fetchNewToken();
  68. return;
  69. }
  70. if (isTokenExpired(currentState)) {
  71. lock.lock();
  72. try {
  73. currentState = tokenState;
  74. if (isTokenExpired(currentState)) {
  75. tokenState = refreshToken(currentState.getRefreshToken());
  76. }
  77. } finally {
  78. lock.unlock();
  79. }
  80. }
  81. }
  82. private boolean isTokenExpired(TokenState state) {
  83. // 添加缓冲时间,避免在token即将过期时使用
  84. return System.currentTimeMillis() > (state.getExpireTime() - EXPIRY_BUFFER);
  85. }
  86. private TokenState loadInitialToken() {
  87. try {
  88. return fetchNewToken();
  89. } catch (Exception e) {
  90. logger.error("Failed to initialize token", e);
  91. throw new RuntimeException("Token initialization failed", e);
  92. }
  93. }
  94. private TokenState fetchNewToken() throws IOException {
  95. JSONObject requestBody = new JSONObject();
  96. requestBody.put("grant_type", "client_credentials");
  97. requestBody.put("app_key", appKey);
  98. requestBody.put("app_secret", appSecret);
  99. return executeTokenRequest(requestBody);
  100. }
  101. private TokenState refreshToken(String refreshToken) throws IOException {
  102. JSONObject requestBody = new JSONObject();
  103. requestBody.put("grant_type", "refresh_token");
  104. requestBody.put("refresh_token", refreshToken);
  105. return executeTokenRequest(requestBody);
  106. }
  107. private TokenState executeTokenRequest(JSONObject requestBody) throws IOException {
  108. Request request = new Request.Builder().url(tokenUrl).post(RequestBody.create(JSON_MEDIA_TYPE, requestBody.toJSONString())).build();
  109. try (Response response = httpClient.newCall(request).execute()) {
  110. if (!response.isSuccessful()) {
  111. String errorBody = response.body() != null ? response.body().string() : "null";
  112. logger.error("Token request failed. Code: " + response.code() + ", Body: " + errorBody);
  113. throw new IOException("Token request failed with code: " + response.code());
  114. }
  115. String responseBody = response.body().string();
  116. JSONObject jsonResponse = JSON.parseObject(responseBody);
  117. String accessToken = jsonResponse.getString("access_token");
  118. String refreshToken = jsonResponse.getString("refresh_token");
  119. Long expiresIn = jsonResponse.getLong("expires_in");
  120. // 更健壮的空值检查
  121. if (accessToken == null || expiresIn == null || expiresIn <= 0) {
  122. throw new IOException("Invalid token response: " + responseBody);
  123. }
  124. return new TokenState(accessToken, System.currentTimeMillis() + TimeUnit.SECONDS.toMillis(expiresIn), refreshToken);
  125. }
  126. }
  127. private OkHttpClient buildHttpClient() {
  128. return new OkHttpClient.Builder().connectTimeout(10, TimeUnit.SECONDS).writeTimeout(10, TimeUnit.SECONDS).readTimeout(30, TimeUnit.SECONDS).retryOnConnectionFailure(true).build();
  129. }
  130. private static class TokenState {
  131. private final String accessToken;
  132. private final long expireTime;
  133. private final String refreshToken;
  134. public TokenState(String accessToken, long expireTime, String refreshToken) {
  135. this.accessToken = accessToken;
  136. this.expireTime = expireTime;
  137. this.refreshToken = refreshToken;
  138. }
  139. public String getAccessToken() {
  140. return accessToken;
  141. }
  142. public long getExpireTime() {
  143. return expireTime;
  144. }
  145. public String getRefreshToken() {
  146. return refreshToken;
  147. }
  148. }
  149. public static class Config {
  150. private static final String BESON_CONFIG_PATH = "/server/properties/beisen/BeiSenConfig.properties";
  151. // public static String APP_KEY = "FFEC5C62AB444567AFC11E8D394CC072";
  152. // public static String APP_SECRET = "74FE75BEE476496EAF531C2219EC10E57EDCE01D74F64557BADEEC78FBFF3B26";
  153. // public static String TOKEN_URL = "https://openapi.italent.cn/token";
  154. // 测试环境
  155. // public static String APP_KEY = "FFEC5C62AB444567AFC11E8D394CC072";
  156. // public static String APP_SECRET = "74FE75BEE476496EAF531C2219EC10E57EDCE01D74F64557BADEEC78FBFF3B26";
  157. // 生产环境
  158. // public static String APP_KEY = "C16FE3483B4E42FCA97686B6D01F1B3C";
  159. // public static String APP_SECRET = "DE0EED6E741149A3842BAA89143415B3D5C76FC304AB4FCF8D4D708B8D1F1E04";
  160. public static String TOKEN_URL = "https://openapi.italent.cn/token";
  161. public static String APP_KEY = "";
  162. public static String APP_SECRET = "";
  163. static {
  164. // 从配置文件中读取配置
  165. try {
  166. BeisenParamByPropertiesUtil beisenParamByProperties = new BeisenParamByPropertiesUtil(BESON_CONFIG_PATH);
  167. Map<String, String> config = beisenParamByProperties.getConfig();
  168. // 如果配置文件中存在配置,则覆盖默认配置
  169. if (config != null) {
  170. // 如果配置文件中存在appKey,则覆盖默认appKey
  171. if (config.containsKey("APP_KEY")) {
  172. APP_KEY = config.get("APP_KEY");
  173. }
  174. // 如果配置文件中存在appSecret,则覆盖默认appSecret
  175. if (config.containsKey("APP_SECRET")) {
  176. APP_SECRET = config.get("APP_SECRET");
  177. }
  178. // 如果配置文件中存在tokenUrl,则覆盖默认tokenUrl
  179. if (config.containsKey("ACCESSTOKEN_URL")) {
  180. TOKEN_URL = config.get("ACCESSTOKEN_URL");
  181. }
  182. }
  183. } catch (IOException e) {
  184. throw new RuntimeException(e);
  185. }
  186. }
  187. }
  188. }