feat: OpenApiInterceptor

This commit is contained in:
tjq 2024-08-10 00:04:12 +08:00
parent bee4795027
commit 53be566173
8 changed files with 111 additions and 33 deletions

View File

@ -1,10 +1,19 @@
package tech.powerjob.client.service.impl;
import com.google.common.collect.Maps;
import lombok.SneakyThrows;
import org.apache.commons.lang3.StringUtils;
import tech.powerjob.client.ClientConfig;
import tech.powerjob.client.module.AppAuthRequest;
import tech.powerjob.client.module.AppAuthResult;
import tech.powerjob.common.OpenAPIConstant;
import tech.powerjob.common.enums.ErrorCodes;
import tech.powerjob.common.exception.PowerJobException;
import tech.powerjob.common.serialize.JsonUtils;
import tech.powerjob.common.utils.DigestUtils;
import java.util.Map;
/**
* 封装鉴权相关逻辑
*
@ -19,15 +28,44 @@ abstract class AppAuthClusterRequestService extends ClusterRequestService {
super(config);
}
protected void refreshAuthInfo() {
@Override
public String request(String path, Object body) {
// 若不存在 appAuthResult则首先进行鉴权
if (appAuthResult == null) {
refreshAppAuthResult();
}
Map<String, String> authHeaders = buildAuthHeader();
String clusterResponse = clusterHaRequest(path, body, authHeaders);
// TODO
return null;
}
private Map<String, String> buildAuthHeader() {
Map<String, String> authHeader = Maps.newHashMap();
authHeader.put(OpenAPIConstant.REQUEST_HEADER_APP_ID, String.valueOf(appAuthResult.getAppId()));
authHeader.put(OpenAPIConstant.REQUEST_HEADER_ACCESS_TOKEN, appAuthResult.getToken());
return authHeader;
}
@SneakyThrows
private void refreshAppAuthResult() {
AppAuthRequest appAuthRequest = buildAppAuthRequest();
String authResponse = clusterHaRequest(OpenAPIConstant.AUTH_APP, appAuthRequest, null);
if (StringUtils.isEmpty(authResponse)) {
throw new PowerJobException(ErrorCodes.CLIENT_HTTP_REQUEST_FAILED, "EMPTY_RESPONSE");
}
this.appAuthResult = JsonUtils.parseObject(authResponse, AppAuthResult.class);
}
protected AppAuthRequest buildAppAuthRequest() {
AppAuthRequest appAuthRequest = new AppAuthRequest();
appAuthRequest.setAppName(config.getAppName());
appAuthRequest.setEncryptedPassword(DigestUtils.md5(config.getPassword()));
try {
} catch (Exception e) {
}
return appAuthRequest;
}
}

View File

@ -1,6 +1,5 @@
package tech.powerjob.client.service.impl;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import tech.powerjob.client.ClientConfig;
@ -32,10 +31,6 @@ abstract class ClusterRequestService implements RequestService {
* 当前地址上次请求成功的地址
*/
protected String currentAddress;
/**
* 鉴权相关 headers
*/
protected Map<String, String> authHeaders = Maps.newHashMap();
/**
* 地址格式
@ -54,10 +49,24 @@ abstract class ClusterRequestService implements RequestService {
this.config = config;
}
protected abstract String sendHttpRequest(String url, String body) throws IOException;
/**
* 具体某一次 HTTP 请求的实现
* @param url 完整请求地址
* @param body 请求体
* @param headers 请求头
* @return 响应
* @throws IOException 异常
*/
protected abstract String sendHttpRequest(String url, String body, Map<String, String> headers) throws IOException;
@Override
public String request(String path, Object obj) {
/**
* 封装集群请求能力
* @param path 请求 PATH
* @param obj 请求体
* @param headers 请求头
* @return 响应
*/
protected String clusterHaRequest(String path, Object obj, Map<String, String> headers) {
String body = obj instanceof String ? (String) obj : JsonUtils.toJSONStringUnsafe(obj);
@ -65,7 +74,7 @@ abstract class ClusterRequestService implements RequestService {
// 先尝试默认地址
String url = getUrl(path, currentAddress);
try {
String res = sendHttpRequest(url, body);
String res = sendHttpRequest(url, body, headers);
if (StringUtils.isNotEmpty(res)) {
return res;
}
@ -80,7 +89,7 @@ abstract class ClusterRequestService implements RequestService {
}
url = getUrl(path, addr);
try {
String res = sendHttpRequest(url, body);
String res = sendHttpRequest(url, body, headers);
if (StringUtils.isNotEmpty(res)) {
log.warn("[ClusterRequestService] server change: from({}) -> to({}).", currentAddress, addr);
currentAddress = addr;

View File

@ -40,13 +40,22 @@ public class ClusterRequestServiceOkHttp3Impl extends ClusterRequestService {
}
@Override
protected String sendHttpRequest(String url, String payload) throws IOException {
public String request(String path, Object body) {
// TODO
return null;
}
@Override
protected String sendHttpRequest(String url, String payload, Map<String, String> h) throws IOException {
// 公共 header
Map<String, String> headers = Maps.newHashMap();
if (config.getDefaultHeaders() != null) {
headers.putAll(config.getDefaultHeaders());
}
if (h != null) {
headers.putAll(h);
}
MediaType jsonType = MediaType.parse(OmsConstant.JSON_MEDIA_TYPE);
RequestBody requestBody = RequestBody.create(jsonType, payload);

View File

@ -61,7 +61,9 @@ public class OpenAPIConstant {
/* ************* 鉴权 ************* */
public static final String HEADER_ACCESS_TOKEN = "X-POWERJOB-ACCESS-TOKEN";
public static final String REQUEST_HEADER_ACCESS_TOKEN = "X-POWERJOB-ACCESS-TOKEN";
public static final String HEADER_APP_ID = "X-POWERJOB-APP-ID";
public static final String REQUEST_HEADER_APP_ID = "X-POWERJOB-APP-ID";
public static final String RESPONSE_HEADER_AUTH_STATUS = "X-POWERJOB-AUTH-PASSED";
}

View File

@ -46,6 +46,11 @@ public enum ErrorCodes {
OPEN_API_PASSWORD_ERROR("-1001", "OPEN_API_PASSWORD_ERROR"),
OPEN_API_AUTH_FAILED("-1002", "OPEN_API_AUTH_FAILED"),
/**
* PowerJobClient 错误码号段
*/
CLIENT_HTTP_REQUEST_FAILED("-2001", "CLIENT_HTTP_REQUEST_FAILED"),
;
private final String code;

View File

@ -2,7 +2,6 @@ package tech.powerjob.common.serialize;
import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature;
@ -86,7 +85,7 @@ public class JsonUtils {
return null;
}
public static <T> T parseObject(String json, Class<T> clz) throws JsonProcessingException {
public static <T> T parseObject(String json, Class<T> clz) throws Exception {
return JSON_MAPPER.readValue(json, clz);
}

View File

@ -1,11 +1,14 @@
package tech.powerjob.server.openapi;
import com.google.common.collect.Sets;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.lang.NonNull;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import tech.powerjob.common.OmsConstant;
import tech.powerjob.common.OpenAPIConstant;
import tech.powerjob.common.exception.PowerJobException;
import tech.powerjob.common.response.PowerResultDTO;
import tech.powerjob.common.serialize.JsonUtils;
@ -15,6 +18,7 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.Set;
/**
* OpenAPI 拦截器
@ -36,6 +40,8 @@ public class OpenApiInterceptor implements HandlerInterceptor {
@Value("${oms.auth.openapi.enable:false}")
private boolean enableOpenApiAuth;
private static final Set<String> IGNORE_OPEN_API_PATH = Sets.newHashSet(OpenAPIConstant.ASSERT, OpenAPIConstant.AUTH_APP);
@Override
public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) throws Exception {
@ -43,15 +49,27 @@ public class OpenApiInterceptor implements HandlerInterceptor {
return true;
}
// 鉴权类请求跳过拦截
String requestURI = request.getRequestURI();
for (String endPath : IGNORE_OPEN_API_PATH) {
if (requestURI.endsWith(endPath)) {
return true;
}
}
try {
openApiSecurityService.authAppByToken(request);
response.addHeader(OpenAPIConstant.RESPONSE_HEADER_AUTH_STATUS, Boolean.TRUE.toString());
} catch (PowerJobException pje) {
PowerResultDTO<Object> ret = PowerResultDTO.f(pje);
writeResponse(JsonUtils.toJSONString(ret), response);
response.addHeader(OpenAPIConstant.RESPONSE_HEADER_AUTH_STATUS, Boolean.FALSE.toString());
writeResponse(PowerResultDTO.f(pje), response);
return false;
} catch (Exception e) {
PowerResultDTO<Object> ret = PowerResultDTO.f(e);
writeResponse(JsonUtils.toJSONString(ret), response);
response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
writeResponse(PowerResultDTO.f(e), response);
log.error("[OpenApiInterceptor] unknown exception when auth app by token", e);
return false;
}
@ -59,16 +77,14 @@ public class OpenApiInterceptor implements HandlerInterceptor {
}
@SneakyThrows
private void writeResponse(String content, HttpServletResponse response) {
// 设置响应状态码通常是 400, 401, 403 等错误码
response.setStatus(HttpServletResponse.SC_FORBIDDEN);
private void writeResponse( PowerResultDTO<Object> powerResult, HttpServletResponse response) {
// 设置响应的 Content-Type
response.setContentType("application/json;charset=UTF-8");
response.setContentType(OmsConstant.JSON_MEDIA_TYPE);
// JSON 写入响应
PrintWriter writer = response.getWriter();
writer.write(content);
writer.write(JsonUtils.toJSONString(powerResult));
writer.flush();
}

View File

@ -44,8 +44,8 @@ public class OpenApiSecurityServiceImpl implements OpenApiSecurityService {
@Override
public void authAppByToken(HttpServletRequest httpServletRequest) {
String token = HttpServletUtils.fetchFromHeader(OpenAPIConstant.HEADER_ACCESS_TOKEN, httpServletRequest);
String appIdFromHeader = HttpServletUtils.fetchFromHeader(OpenAPIConstant.HEADER_APP_ID, httpServletRequest);
String token = HttpServletUtils.fetchFromHeader(OpenAPIConstant.REQUEST_HEADER_ACCESS_TOKEN, httpServletRequest);
String appIdFromHeader = HttpServletUtils.fetchFromHeader(OpenAPIConstant.REQUEST_HEADER_APP_ID, httpServletRequest);
if (StringUtils.isEmpty(appIdFromHeader)) {
throw new PowerJobException(ErrorCodes.INVALID_REQUEST, "lack_of_appId_in_header");