Merge branch 'dev' into 'test'

修复数据总线访问日志无法显示状态码问题:...

See merge request jygk/dsc!22
This commit is contained in:
朝锦 杨
2026-01-22 02:40:04 +00:00
19 changed files with 677 additions and 15 deletions

View File

@@ -0,0 +1,45 @@
package com.zt.plat.module.databus.api;
import com.zt.plat.framework.common.pojo.CommonResult;
import com.zt.plat.framework.common.util.monitor.TracerUtils;
import com.zt.plat.framework.common.util.object.BeanUtils;
import com.zt.plat.framework.common.util.servlet.ServletUtils;
import com.zt.plat.module.databus.api.dto.ApiAccessLogCreateReq;
import com.zt.plat.module.databus.api.provider.DatabusAccessLogProviderApi;
import com.zt.plat.module.databus.dal.dataobject.gateway.ApiAccessLogDO;
import com.zt.plat.module.databus.service.gateway.ApiAccessLogService;
import jakarta.annotation.Resource;
import jakarta.annotation.security.PermitAll;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Map;
import static com.zt.plat.framework.common.pojo.CommonResult.success;
/**
*
* 2026/1/21 10:05
*/
@RestController
public class DatabusAccessLogProviderApiImpl implements DatabusAccessLogProviderApi {
@Resource
private ApiAccessLogService apiAccessLogService;
@Override
@PermitAll
public CommonResult<Boolean> add(Map<String, String> headers, ApiAccessLogCreateReq req) {
ApiAccessLogDO logDO = new ApiAccessLogDO();
BeanUtils.copyProperties(req, logDO);
logDO.setTraceId(TracerUtils.getTraceId());
logDO.setClientIp(ServletUtils.getClientIP());
logDO.setCreateTime(LocalDateTime.now());
logDO.setUpdateTime(LocalDateTime.now());
logDO.setCreator("1");
logDO.setUpdater("1");
apiAccessLogService.create(logDO);
return success(Boolean.TRUE);
}
}

View File

@@ -159,6 +159,7 @@ public class ApiGatewayAccessLogger {
update.setStatus(resolveStatus(status));
update.setResponseTime(LocalDateTime.now());
update.setDuration(calculateDuration(request));
update.setUpdater("1");
apiAccessLogService.update(update);
} catch (Exception ex) {
log.warn("更新入口 API 访问日志失败, logId={}", logId, ex);

View File

@@ -89,6 +89,7 @@ public class ApiGatewayExecutionService {
} else {
responseContext = apiFlowDispatcher.dispatch(context.getApiCode(), context.getApiVersion(), context);
}
responseContext.setResponseStatus(resolveStatus(responseContext));
} catch (ServiceException ex) {
errorProcessor.applyServiceException(context, ex);
accessLogger.onException(context, ex);

View File

@@ -77,6 +77,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
filterChain.doFilter(request, response);
return;
}
// 添加日志接口需要特殊处理
boolean isNormalProcess = !pathMatcher.match("/databus/api/portal/access-log/**", pathWithinApplication);
Long accessLogId = null;
ApiGatewayProperties.Security security = properties.getSecurity();
ApiClientCredentialDO credential = null;
@@ -95,8 +98,10 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogger.finalizeEarly(request, HttpStatus.FORBIDDEN.value(), "IP 禁止访问");
return;
}
// IP 校验通过后再补录入口日志,避免无租户信息写库
accessLogId = accessLogger.logEntrance(request);
// IP 校验通过后再补录入口日志,避免无租户信息写库, 非日志添加接口才记录日志
if (isNormalProcess) {
accessLogId = accessLogger.logEntrance(request);
}
if (!security.isEnabled()) {
byte[] originalBody = StreamUtils.copyToByteArray(request.getInputStream());
CachedBodyHttpServletRequest passthroughRequest = new CachedBodyHttpServletRequest(request, originalBody);
@@ -128,13 +133,17 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
if (nonce.length() < 8) {
throw new SecurityValidationException(HttpStatus.BAD_REQUEST, "随机数长度不足");
}
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
if (isNormalProcess) {
// 非日志添加接口才处理
String signature = requireHeader(request, SIGNATURE_HEADER, "缺少签名");
// 尝试按凭证配置解密请求体,并构建签名载荷进行校验
byte[] decryptedBody = decryptRequestBody(requestBody, credential, security);
verifySignature(request, decryptedBody, signature, credential, security, appId, timestampHeader);
requestBody = decryptedBody;
}
ensureNonce(tenantId, appId, nonce, security);
requestBody = decryptedBody;
}
// 使用可重复读取的请求包装,供后续过滤器继续消费
@@ -154,7 +163,9 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
try {
filterChain.doFilter(securedRequest, responseWrapper);
dispatchedToGateway = true;
encryptResponse(responseWrapper, credential, security);
if (isNormalProcess) {
encryptResponse(responseWrapper, credential, security);
}
} finally {
responseWrapper.copyBodyToResponse();
}
@@ -163,7 +174,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.warn("[API-PORTAL] 安全校验失败: {}", ex.getMessage());
writeErrorResponse(response, security, credential, ex.status(), ex.getMessage());
writeErrorResponse(response, security, credential, ex.status(), ex.getMessage(), isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, ex.status().value(), ex.getMessage());
}
@@ -172,7 +183,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
accessLogId = accessLogger.logEntrance(request);
}
log.error("[API-PORTAL] 处理安全校验时出现异常", ex);
writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败");
writeErrorResponse(response, security, credential, HttpStatus.INTERNAL_SERVER_ERROR, "网关安全校验失败", isNormalProcess);
if (!dispatchedToGateway) {
accessLogger.finalizeEarly(request, HttpStatus.INTERNAL_SERVER_ERROR.value(), "网关安全校验失败");
}
@@ -479,6 +490,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
String token = tokenOptional.get();
securedRequest.setHeader(HttpHeaders.AUTHORIZATION, "Bearer " + token);
securedRequest.setHeader(GatewayJwtResolver.HEADER_ZT_AUTH_TOKEN, token);
}
private static final class SecurityValidationException extends RuntimeException {
@@ -499,7 +511,8 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
ApiGatewayProperties.Security security,
ApiClientCredentialDO credential,
HttpStatus status,
String message) {
String message,
boolean isNormalProcess) {
if (response.isCommitted()) {
log.warn("[API-PORTAL] 响应已提交,无法写入安全校验错误: {}", message);
return;
@@ -514,7 +527,7 @@ public class GatewaySecurityFilter extends OncePerRequestFilter {
.response(null)
.traceId(traceId)
.build();
if (shouldEncryptErrorResponse(security, credential)) {
if (shouldEncryptErrorResponse(security, credential) && isNormalProcess) {
String encryptionKey = credential.getEncryptionKey();
String encryptionType = resolveEncryptionType(credential, security);
try {