fix: PowerJobClient refresh token failed when jwt expired

This commit is contained in:
tjq 2024-08-11 23:00:37 +08:00
parent 44ef76328b
commit 85f5faaaac
10 changed files with 119 additions and 12 deletions

View File

@ -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<AppAuthResult> 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();
}

View File

@ -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

View File

@ -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");
}
}

View File

@ -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<JobInfoDTO> jobInfoDTOResultDTO = powerJobClient.fetchJob(1L);
log.info("[TestClusterHA] response: {}", JSONObject.toJSONString(jobInfoDTOResultDTO));
if (!jobInfoDTOResultDTO.isSuccess()) {
throw new RuntimeException("request failed!");
}
}
}
}

View File

@ -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"),
/**
* 系统内部异常
*/

View File

@ -12,5 +12,5 @@ public interface JwtService {
String build(Map<String, Object> body, String extraSk);
Map<String, Object> parse(String jwt, String extraSk);
ParseResult parse(String jwt, String extraSk);
}

View File

@ -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<String, Object> result;
private String msg;
public enum Status {
SUCCESS,
EXPIRED,
FAILED
}
}

View File

@ -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<String, Object> parse(String jwt, String extraSk) {
return innerParse(fetchSk(extraSk), jwt);
public ParseResult parse(String jwt, String extraSk) {
try {
Map<String, Object> 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) {

View File

@ -236,7 +236,7 @@ public class PowerJobLoginServiceImpl implements PowerJobLoginService {
if (StringUtils.isEmpty(jwtStr)) {
return Optional.empty();
}
final Map<String, Object> jwtBodyMap = jwtService.parse(jwtStr, null);
final Map<String, Object> jwtBodyMap = jwtService.parse(jwtStr, null).getResult();
if (MapUtils.isEmpty(jwtBodyMap)) {
return Optional.empty();

View File

@ -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<String, Object> 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<String, Object> jwtResult = parseResult.getResult();
Long appIdFromJwt = MapUtils.getLong(jwtResult, JWT_KEY_APP_ID);
String passwordFromJwt = MapUtils.getString(jwtResult, JWT_KEY_APP_PASSWORD);