审计日志封装

286 阅读1分钟

1、封装注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface AuditLog {

    /**
     * 操作
     */
    BehaviorEnum behavior() default BehaviorEnum.NULL;

    /**
     * 操作对象
     */
    String object() default "";
    /**
     * 操作详情
     */
    String detail() default "";

}

2、封装日志对象

@Data
public class LogContent {

    /**
     * 项目名称
     **/
    private String applicationName;

    /**
     * 类名
     **/
    private String className;

    /**
     * 方法名
     **/
    private String methodName;

    /**
     * 参数
     **/
    private String args;

    /**
     * 对象
     **/
    private String object;

    /**
     * 行为
     **/
    private String behavior;

    /**
     * 操作详情
     **/
    private String detail;

    /**
     * 用户编号
     */
    private Long userId;
    /**
     * 操作人名称
     **/
    private String userName;

    /**
     * 操作时间
     */
    private Date operatorTime;

}

3、封装行为枚举

public enum BehaviorEnum {
    SAVE(1, "新建"),
    UPDATE(2, "编辑"),
    DELETE(3, "删除"),
    EXCEL_IMPORT(6, "导入"),
    EXCEL_EXPORT(7, "导出"),
    LOCK(11, "锁定"),
    UNLOCK(12, "解锁"),
    UPLOAD(18, "上传"),
    PUT_ON_SALE(22, "上架"),
    PUT_OFF_SALE(23, "下架"),
    START(24, "启用"),
    STOP(25, "停用"),
    FORBID(35, "禁用"),
    RESET(36, "重置"),
    COPY(37, "复制"),
    NULL(0, "null");

    private int type;
    private String name;

    BehaviorEnum(int type, String name) {
        this.type = type;
        this.name = name;
    }

    public int getType() {
        return type;
    }

    public String getName() {
        return name;
    }

    public static BehaviorEnum valueOf(int type) {
        for (BehaviorEnum behaviorEnum : BehaviorEnum.values()) {
            if (behaviorEnum.getType() == type) {
                return behaviorEnum;
            }
        }
        return NULL;
    }
}

4、日志切面

@Slf4j
@Aspect
@Component
public class AuditLogAspect {

    @Value("${spring.application.name}")
    private String applicationName;

    private List<LogContent> LogContentList;

    @Pointcut("@within(auditLog) || @annotation(auditLog)")
    public void point(AuditLog auditLog) {
    }

    @SuppressWarnings("unchecked")
    @Before(value = "point(auditLog)", argNames = "joinPoint,auditLog")
    public void beforeMethod(JoinPoint joinPoint, AuditLog auditLog) {
        if (auditLog == null) {
            return;
        }
        LogContentList = new ArrayList<>();
        LogContent logContent = getLog(joinPoint);
        String object = auditLog.object();
        String detail = auditLog.detail();
        logContent.setDetail(detail);
        logContent.setObject(object);
        logContent.setBehavior(auditLog.behavior().getName());
        LogContentList.add(logContent);

    }

    @AfterReturning(value = "point(auditLog)", argNames = "auditLog,val", returning = "val")
    public void afterMethodReturning(AuditLog auditLog, Object val) {
        if (auditLog == null) {
            return;
        }
        try {
            for (LogContent logContent : LogContentList) {
                //TODO 此处保存至数据库或者发送mq
            }
        } catch (Exception e) {
            log.error("日志记录异常:{}", e);
        }
    }


    private LogContent getLog(JoinPoint joinPoint) {
        LogContent logContent = new LogContent();
        logContent.setOperatorTime(new Date());
        Object[] args = joinPoint.getArgs();
        List<Object> objectList = new ArrayList<>(args.length);
        for (Object arg : args) {
            boolean parse = parse(arg);
            if (parse) {
                objectList.add(arg);
            }
        }
        logContent.setArgs(JSON.toJSONString(objectList));
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        logContent.setClassName(methodSignature.getDeclaringTypeName());
        logContent.setMethodName(methodSignature.getName());
        logContent.setApplicationName(applicationName);
        //此处增加用户信息
        return logContent;
    }

    /**
     * 数据是否可以序列化
     *
     * @param object
     * @return true
     */
    private boolean parse(Object object) {
        if (object instanceof HttpServletRequest) {
            return false;
        } else if (object instanceof HttpServletResponse) {
            return false;
        } else if (object instanceof MultipartFile) {
            return false;
        } else if (object instanceof OutputStream) {
            return false;
        }
        return true;
    }
}

5、使用

image.png

image.png