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 new file mode 100644 index 0000000..bfacf64 --- /dev/null +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/audit/OperationLogAspect.java @@ -0,0 +1,125 @@ +package cn.novalon.manage.sys.audit; + +import cn.novalon.manage.sys.core.domain.OperationLog; +import cn.novalon.manage.sys.core.service.IOperationLogService; +import cn.novalon.manage.sys.util.IpUtils; +import com.fasterxml.jackson.databind.ObjectMapper; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.slf4j.Logger; +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.Mono; +import reactor.core.scheduler.Schedulers; + +@Aspect +@Component +public class OperationLogAspect { + private static final Logger logger = LoggerFactory.getLogger(OperationLogAspect.class); + private final IOperationLogService logService; + private final ObjectMapper objectMapper; + + public OperationLogAspect(IOperationLogService logService, ObjectMapper objectMapper) { + this.logService = logService; + this.objectMapper = objectMapper; + } + + @Around("@annotation(operationLogAnnotation)") + 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(); + 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 result; + } catch (Throwable error) { + status = "1"; + errorMsg = error.getMessage(); + long duration = System.currentTimeMillis() - startTime; + saveLogAsync(operationLogAnnotation, username, ip, method, params, null, duration, status, errorMsg); + throw error; + } + } + + private ServerRequest extractServerRequest(Object[] args) { + if (args == null || args.length == 0) return null; + for (Object arg : args) { + if (arg instanceof ServerRequest) return (ServerRequest) arg; + } + 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 String serializeParams(Object[] args) { + try { + if (args == null || args.length == 0) return null; + return objectMapper.writeValueAsString(args); + } catch (Exception e) { + logger.warn("处的包正数失败: {}", e.getMessage()); + return null; + } + } + + private String serializeResult(Object result) { + try { + if (result == null) return null; + return objectMapper.writeValueAsString(result); + } catch (Exception e) { + 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(); + } +} \ 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 new file mode 100644 index 0000000..451ae69 --- /dev/null +++ b/novalon-manage-api/manage-sys/src/main/java/cn/novalon/manage/sys/util/IpUtils.java @@ -0,0 +1,66 @@ +package cn.novalon.manage.sys.util; + +import org.springframework.web.reactive.function.server.ServerRequest; +import java.net.InetSocketAddress; +import java.util.Optional; + +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"; + + 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; + } + + private static String getXForwardedForIp(ServerRequest request) { + String ip = request.headers().firstHeader("X-Forwarded-For"); + if (ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip)) { + int index = ip.indexOf(","); + if (index != -1) { + return ip.substring(0, index); + } + return ip; + } + return null; + } + + private static String getXRealIp(ServerRequest request) { + String ip = request.headers().firstHeader("X-Real-IP"); + if (ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip)) { + return ip; + } + return null; + } + + private static String getRemoteAddress(ServerRequest request) { + Optional remoteAddress = request.remoteAddress(); + if (remoteAddress.isPresent()) { + String ip = remoteAddress.get().getAddress().getHostAddress(); + if (LOCALHOST_IPV6.equals(ip)) { + ip = LOCALHOST_IP; + } + return ip; + } + return null; + } + + private static boolean isValidIp(String ip) { + return ip != null && ip.length() > 0 && !UNKNOWN.equalsIgnoreCase(ip); + } +}