feat: add operation log filter for automatic logging

This commit is contained in:
张翔
2026-03-18 22:36:40 +08:00
parent bbcaee1116
commit fd1c700412
@@ -0,0 +1,120 @@
package cn.novalon.manage.sys.interceptor;
import cn.novalon.manage.sys.core.domain.OperationLog;
import cn.novalon.manage.sys.core.service.IOperationLogService;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.security.core.context.ReactiveSecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import org.springframework.web.server.WebFilter;
import org.springframework.web.server.WebFilterChain;
import reactor.core.publisher.Mono;
import java.time.Duration;
/**
* 操作日志过滤器
*
* 文件定义:拦截HTTP请求,自动记录操作日志
* 涉及业务:操作日志的自动记录和持久化
* 算法:使用WebFlux的WebFilter机制拦截请求,异步记录日志
*
* @author 张翔
* @date 2026-03-18
*/
@Component
public class OperationLogFilter implements WebFilter {
private static final Logger logger = LoggerFactory.getLogger(OperationLogFilter.class);
private final IOperationLogService logService;
private final ObjectMapper objectMapper;
public OperationLogFilter(IOperationLogService logService, ObjectMapper objectMapper) {
this.logService = logService;
this.objectMapper = objectMapper;
}
@Override
public Mono<Void> filter(ServerWebExchange exchange, WebFilterChain chain) {
long startTime = System.currentTimeMillis();
ServerHttpRequest request = exchange.getRequest();
String path = request.getPath().value();
String method = request.getMethod().name();
String ip = getClientIp(request);
return chain.filter(exchange)
.doOnSuccess(v -> {
long duration = System.currentTimeMillis() - startTime;
recordLog(exchange, path, method, ip, duration, null);
})
.doOnError(error -> {
long duration = System.currentTimeMillis() - startTime;
recordLog(exchange, path, method, ip, duration, error.getMessage());
});
}
private void recordLog(ServerWebExchange exchange, String path, String method, String ip, long duration, String errorMsg) {
try {
OperationLog log = new OperationLog();
log.setOperation(path);
log.setMethod(method);
log.setIp(ip);
log.setDuration(duration);
if (errorMsg != null) {
log.setStatus("1");
log.setErrorMsg(errorMsg);
log.setResult("Failed");
} else {
log.setStatus("0");
log.setResult("Success");
}
String queryParams = exchange.getRequest().getQueryParams().toSingleValueMap().toString();
log.setParams(queryParams);
ReactiveSecurityContextHolder.getContext()
.flatMap(securityContext -> {
Object principal = securityContext.getAuthentication().getPrincipal();
if (principal instanceof String) {
log.setUsername((String) principal);
}
return Mono.empty();
})
.then(Mono.fromRunnable(() -> {
logService.save(log)
.doOnSuccess(saved -> logger.debug("操作日志记录成功: {}", log.getOperation()))
.doOnError(error -> logger.error("操作日志记录失败: {}", error.getMessage()))
.subscribe();
}))
.subscribe();
} catch (Exception e) {
logger.error("记录操作日志时发生异常: {}", e.getMessage());
}
}
private String getClientIp(ServerHttpRequest request) {
String ip = request.getHeaders().getFirst("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeaders().getFirst("WL-Proxy-Client-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddress() != null ? request.getRemoteAddress().getAddress().getHostAddress() : "";
}
if (ip != null && ip.contains(",")) {
ip = ip.split(",")[0].trim();
}
return ip;
}
}