From 85f5faaaacdb2370619b978be5ea286cb76414f4 Mon Sep 17 00:00:00 2001 From: tjq Date: Sun, 11 Aug 2024 23:00:37 +0800 Subject: [PATCH] fix: PowerJobClient refresh token failed when jwt expired --- .../impl/AppAuthClusterRequestService.java | 9 +++-- .../ClusterRequestServiceOkHttp3Impl.java | 6 ++-- .../client/test/ClientInitializer.java | 3 +- .../powerjob/client/test/TestClusterHA.java | 35 +++++++++++++++++++ .../powerjob/common/enums/ErrorCodes.java | 12 ++++++- .../powerjob/server/auth/jwt/JwtService.java | 2 +- .../powerjob/server/auth/jwt/ParseResult.java | 35 +++++++++++++++++++ .../server/auth/jwt/impl/JwtServiceImpl.java | 16 +++++++-- .../login/impl/PowerJobLoginServiceImpl.java | 2 +- .../security/OpenApiSecurityServiceImpl.java | 11 +++++- 10 files changed, 119 insertions(+), 12 deletions(-) create mode 100644 powerjob-client/src/test/java/tech/powerjob/client/test/TestClusterHA.java create mode 100644 powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/ParseResult.java diff --git a/powerjob-client/src/main/java/tech/powerjob/client/service/impl/AppAuthClusterRequestService.java b/powerjob-client/src/main/java/tech/powerjob/client/service/impl/AppAuthClusterRequestService.java index ee7cff18..241460a6 100644 --- a/powerjob-client/src/main/java/tech/powerjob/client/service/impl/AppAuthClusterRequestService.java +++ b/powerjob-client/src/main/java/tech/powerjob/client/service/impl/AppAuthClusterRequestService.java @@ -3,6 +3,7 @@ package tech.powerjob.client.service.impl; import com.alibaba.fastjson.JSONObject; import com.google.common.collect.Maps; import lombok.SneakyThrows; +import lombok.extern.slf4j.Slf4j; import tech.powerjob.client.ClientConfig; import tech.powerjob.client.TypeStore; import tech.powerjob.client.module.AppAuthRequest; @@ -24,6 +25,7 @@ import java.util.Map; * @author tjq * @since 2024/2/21 */ +@Slf4j abstract class AppAuthClusterRequestService extends ClusterRequestService { protected AppAuthResult appAuthResult; @@ -49,6 +51,7 @@ abstract class AppAuthClusterRequestService extends ClusterRequestService { } // 否则请求无效,刷新鉴权后重新请求 + log.warn("[PowerJobClient] auth failed[authStatus: {}], try to refresh the auth info", authStatus); refreshAppAuthResult(); httpResponse = doRequest(path, powerRequestBody); @@ -83,12 +86,14 @@ abstract class AppAuthClusterRequestService extends ClusterRequestService { AppAuthRequest appAuthRequest = buildAppAuthRequest(); HttpResponse httpResponse = clusterHaRequest(OpenAPIConstant.AUTH_APP, PowerRequestBody.newJsonRequestBody(appAuthRequest)); if (!httpResponse.isSuccess()) { - throw new PowerJobException("auth_app_exception!"); + throw new PowerJobException("AUTH_APP_EXCEPTION!"); } ResultDTO authResultDTO = JSONObject.parseObject(httpResponse.getResponse(), TypeStore.APP_AUTH_RESULT_TYPE); if (!authResultDTO.isSuccess()) { - throw new PowerJobException("auth_failed:" + authResultDTO.getMessage()); + throw new PowerJobException("AUTH_FAILED_" + authResultDTO.getMessage()); } + + log.warn("[PowerJobClient] refresh auth info successfully!"); this.appAuthResult = authResultDTO.getData(); } diff --git a/powerjob-client/src/main/java/tech/powerjob/client/service/impl/ClusterRequestServiceOkHttp3Impl.java b/powerjob-client/src/main/java/tech/powerjob/client/service/impl/ClusterRequestServiceOkHttp3Impl.java index 1c05289d..ff24f8d9 100644 --- a/powerjob-client/src/main/java/tech/powerjob/client/service/impl/ClusterRequestServiceOkHttp3Impl.java +++ b/powerjob-client/src/main/java/tech/powerjob/client/service/impl/ClusterRequestServiceOkHttp3Impl.java @@ -126,10 +126,10 @@ public class ClusterRequestServiceOkHttp3Impl extends AppAuthClusterRequestServi // 设置读取超时时间 .readTimeout(Optional.ofNullable(config.getReadTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS) // 设置写的超时时间 - .writeTimeout(Optional.ofNullable(config.getReadTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS) + .writeTimeout(Optional.ofNullable(config.getWriteTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS) // 设置连接超时时间 - .connectTimeout(Optional.ofNullable(config.getReadTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS) - .callTimeout(Optional.ofNullable(config.getReadTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS); + .connectTimeout(Optional.ofNullable(config.getConnectionTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS) + .callTimeout(Optional.ofNullable(config.getConnectionTimeout()).orElse(DEFAULT_TIMEOUT_SECONDS), TimeUnit.SECONDS); } @Override diff --git a/powerjob-client/src/test/java/tech/powerjob/client/test/ClientInitializer.java b/powerjob-client/src/test/java/tech/powerjob/client/test/ClientInitializer.java index d22d8955..4cad73ba 100644 --- a/powerjob-client/src/test/java/tech/powerjob/client/test/ClientInitializer.java +++ b/powerjob-client/src/test/java/tech/powerjob/client/test/ClientInitializer.java @@ -1,5 +1,6 @@ package tech.powerjob.client.test; +import com.google.common.collect.Lists; import org.junit.jupiter.api.BeforeAll; import tech.powerjob.client.IPowerJobClient; import tech.powerjob.client.PowerJobClient; @@ -16,6 +17,6 @@ public class ClientInitializer { @BeforeAll public static void initClient() throws Exception { - powerJobClient = new PowerJobClient("127.0.0.1:7700", "powerjob-worker-samples", "powerjob12345"); + powerJobClient = new PowerJobClient(Lists.newArrayList("127.0.0.1:7700", "127.0.0.1:7701"), "powerjob-worker-samples", "powerjob123"); } } diff --git a/powerjob-client/src/test/java/tech/powerjob/client/test/TestClusterHA.java b/powerjob-client/src/test/java/tech/powerjob/client/test/TestClusterHA.java new file mode 100644 index 00000000..e10cc8d3 --- /dev/null +++ b/powerjob-client/src/test/java/tech/powerjob/client/test/TestClusterHA.java @@ -0,0 +1,35 @@ +package tech.powerjob.client.test; + +import com.alibaba.fastjson.JSONObject; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.Test; +import tech.powerjob.common.response.JobInfoDTO; +import tech.powerjob.common.response.ResultDTO; +import tech.powerjob.common.utils.CommonUtils; + +/** + * 测试容灾能力 + * + * @author tjq + * @since 2024/8/11 + */ +@Slf4j +public class TestClusterHA extends ClientInitializer { + + @Test + void testHa() { + // 人工让 server 启停 + for (int i = 0; i < 1000000; i++) { + + CommonUtils.easySleep(100); + + ResultDTO jobInfoDTOResultDTO = powerJobClient.fetchJob(1L); + + log.info("[TestClusterHA] response: {}", JSONObject.toJSONString(jobInfoDTOResultDTO)); + + if (!jobInfoDTOResultDTO.isSuccess()) { + throw new RuntimeException("request failed!"); + } + } + } +} diff --git a/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java b/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java index b01035b0..db0043c6 100644 --- a/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java +++ b/powerjob-common/src/main/java/tech/powerjob/common/enums/ErrorCodes.java @@ -31,10 +31,20 @@ public enum ErrorCodes { INCORRECT_PASSWORD("-400", "INCORRECT_PASSWORD"), + /** + * 非法令牌 + */ INVALID_TOKEN("-401", "INVALID_TOKEN"), - + /** + * 无效 APP(无法找到 app) + */ INVALID_APP("-402", "INVALID_APP"), + /** + * 令牌过期 + */ + TOKEN_EXPIRED("-403", "TOKEN_EXPIRED"), + /** * 系统内部异常 */ diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/JwtService.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/JwtService.java index b6ea29a8..28564b08 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/JwtService.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/JwtService.java @@ -12,5 +12,5 @@ public interface JwtService { String build(Map body, String extraSk); - Map parse(String jwt, String extraSk); + ParseResult parse(String jwt, String extraSk); } diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/ParseResult.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/ParseResult.java new file mode 100644 index 00000000..04b39768 --- /dev/null +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/ParseResult.java @@ -0,0 +1,35 @@ +package tech.powerjob.server.auth.jwt; + +import lombok.Data; +import lombok.experimental.Accessors; + +import java.io.Serializable; +import java.util.Map; + +/** + * 解析结果 + * + * @author tjq + * @since 2024/8/11 + */ +@Data +@Accessors(chain = true) +public class ParseResult implements Serializable { + + /** + * 解析状态 + */ + private Status status; + /** + * 解析结果 + */ + private Map result; + + private String msg; + + public enum Status { + SUCCESS, + EXPIRED, + FAILED + } +} diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java index 15cb49bb..871ea236 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/jwt/impl/JwtServiceImpl.java @@ -4,10 +4,13 @@ import com.google.common.collect.Maps; import io.jsonwebtoken.*; import io.jsonwebtoken.io.Decoders; import io.jsonwebtoken.security.Keys; +import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.exception.ExceptionUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import tech.powerjob.server.auth.jwt.JwtService; +import tech.powerjob.server.auth.jwt.ParseResult; import tech.powerjob.server.auth.jwt.SecretProvider; import javax.annotation.Resource; @@ -22,6 +25,7 @@ import java.util.UUID; * @author tjq * @since 2023/3/20 */ +@Slf4j @Service public class JwtServiceImpl implements JwtService { @@ -63,8 +67,16 @@ public class JwtServiceImpl implements JwtService { } @Override - public Map parse(String jwt, String extraSk) { - return innerParse(fetchSk(extraSk), jwt); + public ParseResult parse(String jwt, String extraSk) { + try { + Map parseResult = innerParse(fetchSk(extraSk), jwt); + return new ParseResult().setStatus(ParseResult.Status.SUCCESS).setResult(parseResult); + } catch (ExpiredJwtException expiredJwtException) { + return new ParseResult().setStatus(ParseResult.Status.EXPIRED).setMsg(expiredJwtException.getMessage()); + } catch (Exception e) { + log.warn("[JwtService] parse jwt[{}] with extraSk[{}] failed", jwt, extraSk, e); + return new ParseResult().setStatus(ParseResult.Status.FAILED).setMsg(ExceptionUtils.getMessage(e)); + } } private String fetchSk(String extraSk) { diff --git a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java index 819cabcf..80aa018e 100644 --- a/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java +++ b/powerjob-server/powerjob-server-auth/src/main/java/tech/powerjob/server/auth/service/login/impl/PowerJobLoginServiceImpl.java @@ -236,7 +236,7 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService { if (StringUtils.isEmpty(jwtStr)) { return Optional.empty(); } - final Map jwtBodyMap = jwtService.parse(jwtStr, null); + final Map jwtBodyMap = jwtService.parse(jwtStr, null).getResult(); if (MapUtils.isEmpty(jwtBodyMap)) { return Optional.empty(); diff --git a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/openapi/security/OpenApiSecurityServiceImpl.java b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/openapi/security/OpenApiSecurityServiceImpl.java index d855bb6b..00c4a762 100644 --- a/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/openapi/security/OpenApiSecurityServiceImpl.java +++ b/powerjob-server/powerjob-server-starter/src/main/java/tech/powerjob/server/openapi/security/OpenApiSecurityServiceImpl.java @@ -12,6 +12,7 @@ import tech.powerjob.common.enums.ErrorCodes; import tech.powerjob.common.exception.PowerJobException; import tech.powerjob.server.auth.common.utils.HttpServletUtils; import tech.powerjob.server.auth.jwt.JwtService; +import tech.powerjob.server.auth.jwt.ParseResult; import tech.powerjob.server.core.service.AppInfoService; import tech.powerjob.server.persistence.remote.model.AppInfoDO; @@ -53,7 +54,15 @@ public class OpenApiSecurityServiceImpl implements OpenApiSecurityService { throw new PowerJobException(ErrorCodes.OPEN_API_AUTH_FAILED, "token_is_empty"); } - Map jwtResult = jwtService.parse(token, null); + ParseResult parseResult = jwtService.parse(token, null); + switch (parseResult.getStatus()) { + case EXPIRED: + throw new PowerJobException(ErrorCodes.TOKEN_EXPIRED, parseResult.getMsg()); + case FAILED: + throw new PowerJobException(ErrorCodes.INVALID_TOKEN, parseResult.getMsg()); + } + + Map jwtResult = parseResult.getResult(); Long appIdFromJwt = MapUtils.getLong(jwtResult, JWT_KEY_APP_ID); String passwordFromJwt = MapUtils.getString(jwtResult, JWT_KEY_APP_PASSWORD);