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; 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.ClientConfig;
import tech.powerjob.client.module.AppAuthRequest; import tech.powerjob.client.module.AppAuthRequest;
import tech.powerjob.client.module.AppAuthResult; 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 tech.powerjob.common.utils.DigestUtils;
import java.util.Map;
/** /**
* 封装鉴权相关逻辑 * 封装鉴权相关逻辑
* *
@ -19,15 +28,44 @@ abstract class AppAuthClusterRequestService extends ClusterRequestService {
super(config); 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 appAuthRequest = new AppAuthRequest();
appAuthRequest.setAppName(config.getAppName()); appAuthRequest.setAppName(config.getAppName());
appAuthRequest.setEncryptedPassword(DigestUtils.md5(config.getPassword())); appAuthRequest.setEncryptedPassword(DigestUtils.md5(config.getPassword()));
return appAuthRequest;
try {
} catch (Exception e) {
}
} }
} }

View File

@ -1,6 +1,5 @@
package tech.powerjob.client.service.impl; package tech.powerjob.client.service.impl;
import com.google.common.collect.Maps;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.StringUtils;
import tech.powerjob.client.ClientConfig; import tech.powerjob.client.ClientConfig;
@ -32,10 +31,6 @@ abstract class ClusterRequestService implements RequestService {
* 当前地址上次请求成功的地址 * 当前地址上次请求成功的地址
*/ */
protected String currentAddress; protected String currentAddress;
/**
* 鉴权相关 headers
*/
protected Map<String, String> authHeaders = Maps.newHashMap();
/** /**
* 地址格式 * 地址格式
@ -54,10 +49,24 @@ abstract class ClusterRequestService implements RequestService {
this.config = config; 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); String body = obj instanceof String ? (String) obj : JsonUtils.toJSONStringUnsafe(obj);
@ -65,7 +74,7 @@ abstract class ClusterRequestService implements RequestService {
// 先尝试默认地址 // 先尝试默认地址
String url = getUrl(path, currentAddress); String url = getUrl(path, currentAddress);
try { try {
String res = sendHttpRequest(url, body); String res = sendHttpRequest(url, body, headers);
if (StringUtils.isNotEmpty(res)) { if (StringUtils.isNotEmpty(res)) {
return res; return res;
} }
@ -80,7 +89,7 @@ abstract class ClusterRequestService implements RequestService {
} }
url = getUrl(path, addr); url = getUrl(path, addr);
try { try {
String res = sendHttpRequest(url, body); String res = sendHttpRequest(url, body, headers);
if (StringUtils.isNotEmpty(res)) { if (StringUtils.isNotEmpty(res)) {
log.warn("[ClusterRequestService] server change: from({}) -> to({}).", currentAddress, addr); log.warn("[ClusterRequestService] server change: from({}) -> to({}).", currentAddress, addr);
currentAddress = addr; currentAddress = addr;

View File

@ -40,13 +40,22 @@ public class ClusterRequestServiceOkHttp3Impl extends ClusterRequestService {
} }
@Override @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 // 公共 header
Map<String, String> headers = Maps.newHashMap(); Map<String, String> headers = Maps.newHashMap();
if (config.getDefaultHeaders() != null) { if (config.getDefaultHeaders() != null) {
headers.putAll(config.getDefaultHeaders()); headers.putAll(config.getDefaultHeaders());
} }
if (h != null) {
headers.putAll(h);
}
MediaType jsonType = MediaType.parse(OmsConstant.JSON_MEDIA_TYPE); MediaType jsonType = MediaType.parse(OmsConstant.JSON_MEDIA_TYPE);
RequestBody requestBody = RequestBody.create(jsonType, payload); 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_PASSWORD_ERROR("-1001", "OPEN_API_PASSWORD_ERROR"),
OPEN_API_AUTH_FAILED("-1002", "OPEN_API_AUTH_FAILED"), OPEN_API_AUTH_FAILED("-1002", "OPEN_API_AUTH_FAILED"),
/**
* PowerJobClient 错误码号段
*/
CLIENT_HTTP_REQUEST_FAILED("-2001", "CLIENT_HTTP_REQUEST_FAILED"),
; ;
private final String code; 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.annotation.JsonInclude;
import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference; import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.MapperFeature;
@ -86,7 +85,7 @@ public class JsonUtils {
return null; 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); return JSON_MAPPER.readValue(json, clz);
} }

View File

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

View File

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