引言
在 Web 应用中,操作日志记录是一项重要的功能。它可以帮助我们跟踪用户的操作行为,为问题排查提供线索,同时也可以作为审计的依据。在Spring框架中,我们可以利用AOP(Aspect-oriented Programming,面向切面编程)来实现这一功能。
创建操作日志记录注解
首先,我们需要创建一个用于记录操作日志的注解,如下所示:
package com.emo.common.core.annotation;
import java.lang.annotation.*;
/**
* 操作日志记录注解
*
* @author J.
*/
@Documented
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface OperationLog {
/**
* 操作功能
*/
String value() default "";
/**
* 操作模块
*/
String module() default "";
/**
* 备注
*/
String comments() default "";
/**
* 是否记录请求参数
*/
boolean param() default true;
/**
* 是否记录返回结果
*/
boolean result() default true;
}
这个注解可以通过 value() 方法来描述操作功能,通过 module() 方法来指定操作模块,通过 comments() 方法来添加备注。同时,我们还可以通过 param() 和 result() 方法来决定是否记录请求参数和返回结果。
实现注解处理器
然后,我们需要创建一个注解处理器,用于处理带有 @OperationLog 注解的方法。在这个处理器中,我们可以使用Spring AOP的功能来获取被注解方法的相关信息,如方法名、参数、返回值等,并将这些信息记录到日志中。
package com.emo.common.core.aspect;
import com.emo.common.core.annotation.OperationLog;
import com.emo.common.system.entity.OperationRecord;
import com.emo.common.system.entity.User;
import com.emo.common.system.service.OperationRecordService;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
@Aspect
@Component
public class OperationLogAspect {
@Resource
private OperationRecordService operationRecordService;
@Pointcut("@annotation(com.eleadmin.common.core.annotation.OperationLog)")
public void operationLog() {
}
@AfterReturning(pointcut = "operationLog()", returning = "result")
public void doAfterReturning(JoinPoint joinPoint, Object result) {
saveLog(joinPoint, result);
}
private void saveLog(JoinPoint joinPoint, Object result) {
OperationRecord record = new OperationRecord();
User user = getLoginUser();
if (user != null) {
record.setUserId(user.getUserId());
}
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = (attributes == null ? null : attributes.getRequest());
if (request != null) {
record.setUrl(request.getRequestURI());
record.setRequestMethod(request.getMethod());
record.setIp(request.getRemoteAddr());
}
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
record.setMethod(joinPoint.getTarget().getClass().getName() + "." + signature.getName());
Method method = signature.getMethod();
if (method != null) {
OperationLog ol = method.getAnnotation(OperationLog.class);
if (ol != null) {
//记录操作功能
record.setDescription(ol.value());
//记录操作模块
record.setModule(ol.module());
// 记录请求参数
// 记录请求结果
}
}
operationRecordService.saveAsync(record);
}
private User getLoginUser() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication != null) {
Object object = authentication.getPrincipal();
if (object instanceof User) {
return (User) object;
}
}
return null;
}
}
在方法上使用注解
@PreAuthorize("hasAuthority('sys:user:remove')")
@OperationLog
@ApiOperation("删除用户")
@DeleteMapping("/{id}")
public ApiResult<?> remove(@PathVariable("id") Integer id) {
if (userService.removeById(id)) {
return success("删除成功");
}
return fail("删除失败");
}
总结
通过以上步骤,我们实现了在Spring AOP中记录操作日志的功能。这种方法的优点是,我们不需要在每个需要记录日志的方法中添加重复的日志记录代码,只需在方法上添加一个注解即可。这样做不仅减少了代码的冗余,还提高了代码的可读性和可维护性。