diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java index bfacf64..e6ebbdb 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java @@ -12,13 +12,16 @@ import org.slf4j.LoggerFactory; import org.springframework.security.core.context.ReactiveSecurityContextHolder; import org.springframework.stereotype.Component; import org.springframework.web.reactive.function.server.ServerRequest; +import reactor.core.publisher.Flux; import reactor.core.publisher.Mono; -import reactor.core.scheduler.Schedulers; @Aspect @Component public class OperationLogAspect { private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class); + private static final int MAX_PARAM_LENGTH = 2000; + private static final int MAX_RESULT_LENGTH = 5000; + private final IOperationLogService logService; private final ObjectMapper objectMapper; @@ -31,32 +34,50 @@ public class OperationLogAspect { public Object around(ProceedingJoinPoint point, cn.novalon.manage.sys.audit.OperationLog operationLogAnnotation) throws Throwable { long startTime = System.currentTimeMillis(); ServerRequest serverRequest = extractServerRequest(point.getArgs()); - String username = getCurrentUsername(); String ip = IpUtils.getClientIp(serverRequest); String method = point.getSignature().toShortString(); String params = serializeParams(point.getArgs()); - Object result = null; - String status = "0"; - String errorMsg = null; + try { - result = point.proceed(); + Object result = point.proceed(); + if (result instanceof Mono) { - return ((Mono) result) - .doOnSuccess(res -> { - long duration = System.currentTimeMillis() - startTime; - saveLogAsync(operationLogAnnotation, username, ip, method, params, res, duration, "0", null); - }) - .doOnError(error -> { - long duration = System.currentTimeMillis() - startTime; - saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, "1", error.getMessage()); - }); + return getCurrentUsername() + .flatMap(username -> ((Mono) result) + .flatMap(res -> { + long duration = System.currentTimeMillis() - startTime; + return saveLogAsync(operationLogAnnotation, username, ip, method, params, res, duration, "0", null) + .thenReturn(res); + }) + .onErrorResume(error -> { + long duration = System.currentTimeMillis() - startTime; + return saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, "1", error.getMessage()) + .then(Mono.error(error)); + }) + ); + } else if (result instanceof Flux) { + return getCurrentUsername() + .flatMapMany(username -> ((Flux) result) + .collectList() + .flatMapMany(res -> { + long duration = System.currentTimeMillis() - startTime; + return saveLogAsync(operationLogAnnotation, username, ip, method, params, res, duration, "0", null) + .thenMany(Flux.fromIterable(res)); + }) + .onErrorResume(error -> { + long duration = System.currentTimeMillis() - startTime; + return saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, "1", error.getMessage()) + .thenMany(Flux.error(error)); + }) + ); } + return result; } catch (Throwable error) { - status = "1"; - errorMsg = error.getMessage(); long duration = System.currentTimeMillis() - startTime; - saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, status, errorMsg); + getCurrentUsername() + .flatMap(username -> saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, "1", error.getMessage())) + .subscribe(); throw error; } } @@ -69,25 +90,24 @@ public class OperationLogAspect { return null; } - private String getCurrentUsername() { - try { - return ReactiveSecurityContextHolder.getContext() - .map(ctx -> ctx.getAuthentication().getPrincipal()) - .cast(String.class) - .blockOptional() - .orElse("system"); - } catch (Exception e) { - logger.warn("考试当前用命名失败: {}", e.getMessage()); - return "system"; - } + private Mono getCurrentUsername() { + return ReactiveSecurityContextHolder.getContext() + .map(ctx -> ctx.getAuthentication().getPrincipal()) + .map(principal -> principal instanceof String ? (String) principal : "system") + .defaultIfEmpty("system") + .onErrorReturn("system"); } private String serializeParams(Object[] args) { try { if (args == null || args.length == 0) return null; - return objectMapper.writeValueAsString(args); + String json = objectMapper.writeValueAsString(args); + if (json.length() > MAX_PARAM_LENGTH) { + return json.substring(0, MAX_PARAM_LENGTH) + "...(truncated)"; + } + return json; } catch (Exception e) { - logger.warn("处的包正数失败: {}", e.getMessage()); + logger.warn("序列化参数失败: {}", e.getMessage()); return null; } } @@ -95,31 +115,36 @@ public class OperationLogAspect { private String serializeResult(Object result) { try { if (result == null) return null; - return objectMapper.writeValueAsString(result); + String json = objectMapper.writeValueAsString(result); + if (json.length() > MAX_RESULT_LENGTH) { + return json.substring(0, MAX_RESULT_LENGTH) + "...(truncated)"; + } + return json; } catch (Exception e) { - logger.warn("处皅包正数失败: {}", e.getMessage()); + logger.warn("序列化结果失败: {}", e.getMessage()); return null; } } - private void saveLogAsync(cn.novalon.manage.sys.audit.OperationLog annotation, String username, String ip, String method, String params, Object result, long duration, String status, String errorMsg) { - Mono.fromRunnable(() -> { - OperationLog log = new OperationLog(); - log.setUsername(username); - log.setOperation(annotation.module() + " - " + annotation.operation()); - log.setMethod(method); - log.setParams(params); - log.setResult(serializeResult(result)); - log.setIp(ip); - log.setDuration(duration); - log.setStatus(status); - log.setErrorMsg(errorMsg); - logService.save(log) - .doOnSuccess(saved -> logger.debug("操作实跋信息成功退: {} - {}", annotation.module(), annotation.operation())) - .doOnError(error -> logger.error("꓍作实跋信息成功退: {}", error.getMessage())) - .subscribe(); - }) - .subscribeOn(Schedulers.boundedElastic()) - .subscribe(); + private Mono saveLogAsync(cn.novalon.manage.sys.audit.OperationLog annotation, + String username, String ip, String method, + String params, Object result, long duration, + String status, String errorMsg) { + OperationLog log = new OperationLog(); + log.setUsername(username); + log.setOperation(annotation.module() + " - " + annotation.operation()); + log.setMethod(method); + log.setParams(params); + log.setResult(serializeResult(result)); + log.setIp(ip); + log.setDuration(duration); + log.setStatus(status); + log.setErrorMsg(errorMsg); + + return logService.save(log) + .doOnSuccess(saved -> logger.debug("操作日志保存成功: {} - {}", + annotation.module(), annotation.operation())) + .doOnError(error -> logger.error("操作日志保存失败: {}", error.getMessage())) + .then(); } -} \ No newline at end of file +} diff --git a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/util/IpUtils.java b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/util/IpUtils.java index 451ae69..f37a2a6 100644 --- a/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/util/IpUtils.java +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/util/IpUtils.java @@ -4,30 +4,55 @@ import org.springframework.web.reactive.function.server.ServerRequest; import java.net.InetSocketAddress; import java.util.Optional; +/** + * IP地址工具类 + * 用于从ServerRequest中获取客户端真实IP地址 + * 支持代理服务器场景(X-Forwarded-For, X-Real-IP) + * + * @author 张翔 + * @date 2026-04-03 + */ public class IpUtils { + private static final String UNKNOWN = "unknown"; private static final String LOCALHOST_IP = "127.0.0.1"; private static final String LOCALHOST_IPV6 = "0:0:0:0:0:0:0:1"; + /** + * 从ServerRequest中获取客户端真实IP地址 + * 支持代理服务器场景,优先级: X-Forwarded-For > X-Real-IP > RemoteAddress + * + * @param request ServerRequest对象 + * @return 客户端IP地址,获取失败返回"unknown" + */ public static String getClientIp(ServerRequest request) { if (request == null) { return UNKNOWN; } + String ip = getXForwardedForIp(request); if (isValidIp(ip)) { return ip; } + ip = getXRealIp(request); if (isValidIp(ip)) { return ip; } + ip = getRemoteAddress(request); if (isValidIp(ip)) { return ip; } + return UNKNOWN; } + /** + * 从X-Forwarded-For头获取IP地址 + * X-Forwarded-For格式: client, proxy1, proxy2 + * 取第一个非unknown的有效IP + */ private static String getXForwardedForIp(ServerRequest request) { String ip = request.headers().firstHeader("X-Forwarded-For"); if (ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip)) { @@ -40,6 +65,9 @@ public class IpUtils { return null; } + /** + * 从X-Real-IP头获取IP地址 + */ private static String getXRealIp(ServerRequest request) { String ip = request.headers().firstHeader("X-Real-IP"); if (ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip)) { @@ -48,6 +76,10 @@ public class IpUtils { return null; } + /** + * 从RemoteAddress获取IP地址 + * 将IPv6本地地址转换为IPv4格式 + */ private static String getRemoteAddress(ServerRequest request) { Optional remoteAddress = request.remoteAddress(); if (remoteAddress.isPresent()) { @@ -60,6 +92,9 @@ public class IpUtils { return null; } + /** + * 验证IP地址是否有效 + */ private static boolean isValidIp(String ip) { return ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip); }