Spring AOP +自定义注解 + Spel表达式 实现审计日志

2023-11-07

1-简介

  • 审计日记就是记录用户的操作记录
  • 基于AOP动态代理 实现自定义审计日志注解, 并支持Spel表达式解析

2-实现

2-1 日志存储实体类


@Data
@Builder
@ToString
public class AuditingLog {

    private String userId;  // 用户id

    private String userNickname; //用户昵称

    private String operationInfo; //操作信息

    private String interfaceName; // 调用的接口方法名

    private String applicationName; // 调用的服务名

    private LocalDateTime createTime; //操作时间
}

2-2 自定义审计日志注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface AuditLog {
    String logInfo(); //日志信息
}

2-3 日志注解的AOP的切面

@Aspect
@Component
public class AuditLogAOP {

   	@Value("${spring.application.name}")
    private String applicationName; //从配置文件获得服务名

    // spel表达式解析器
    private SpelExpressionParser spelExpressionParser = new SpelExpressionParser();

    // 参数名发现器
    private DefaultParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();

    @Before(value = "@annotation(enableAuditLog) || @within(enableAuditLog)")
    public void getAutiLogInfo(JoinPoint joinPoint, AuditLog enableAuditLog){

        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        if (enableAuditLog == null) {
            enableAuditLog = signature.getMethod().getAnnotation(AuditLog.class);
        }

		// 构建日志存储对象
        AuditingLog auditlog = AuditingLog.builder().applicationName(applicationName).createTime(LocalDateTime.now()).build();

		auditlog.setUserId(xxx);  // 从上下文获取当前操作的用户信息
	    auditlog.setUserNickname(xx);
        
	// 设置操作的接口方法名        
	auditlog.setInterfaceName(signature.getDeclaringTypeName()+"."+signature.getName());

		// 获得日志注解上自定义的日志信息
        String logInfo = enableAuditLog.logInfo();

		// Spel表达式解析日志信息
        // 获得方法参数名数组
        String[] parameterNames = parameterNameDiscoverer.getParameterNames(signature.getMethod());
        if (parameterNames != null && parameterNames.length > 0){
            EvaluationContext context = new StandardEvaluationContext();

            //获取方法参数值
            Object[] args = joinPoint.getArgs();
            for (int i = 0; i < args.length; i++) {
                context.setVariable(parameterNames[i],args[i]); // 替换spel里的变量值为实际值, 比如 #user -->  user对象
            }

            // 解析出实际的日志信息
            String opeationInfo = spelExpressionParser.parseExpression(logInfo).getValue(context).toString();
            auditlog.setOperationInfo(opeationInfo);
        }

        // 打印日志信息
        log.info(auditlog.toString());

        //TODO 这时可以将日志信息auditlog进行异步存储,比如写入到文件通过logstash增量的同步到Elasticsearch或者DB

    }
}

2-4 开启审计日志功能

  • 在分布式项目中一般会将日志抽离出来公共调用, 所以为了方便的注入审计日志功能,可以编写对应 Enable注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import({AuditLogAOP.class}) // 注入AOP切面到容器
public @interface EnableAuditLog {

}

3 使用

3-1 开启审计日志功能

  • 在要使用审计日志功能的服务的入口类开启审计日志功能

比如

@SpringBootApplication
@EnableDiscoveryClient
@EnableAuditLog //开启审计日志
public class UmsAdminApplication {
    public static void main(String[] args) {
        SpringApplication.run(UmsAdminApplication.class,args);
    }
}


3-2 在接口上使用

比如:

 	@AuditLog(logInfo = "'新增管理员:'+ #user.username")
    @PostMapping
    public String addUser(@RequestBody User user){
    
        return null;
    }

打赏

如果觉得文章有用,你可鼓励下作者

在这里插入图片描述

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring AOP +自定义注解 + Spel表达式 实现审计日志 的相关文章

  • Java:迭代 Collection 的最佳方法(此处为 ArrayList)

    今天 当我看到一段我已经使用了数百次的代码时 我很高兴地开始编码 迭代集合 此处为 ArrayList 出于某种原因 我实际上查看了 Eclipse 的自动完成选项 这让我想知道 在什么情况下以下循环比其他循环更好使用 经典的数组索引循环
  • Android在排序列表时忽略大小写

    我有一个名为路径的列表 我目前正在使用以下代码对字符串进行排序 java util Collections sort path 这工作正常 它对我的 列表进行排序 但是它以不同的方式处理第一个字母的情况 即它用大写字母对列表进行排序 然后用
  • 如何使用 Java 处理 Selenium WebDriver 中的新窗口?

    这是我的代码 driver findElement By id ImageButton5 click Thread sleep 3000 String winHandleBefore driver getWindowHandle drive
  • Java AES 128 加密方式与 openssl 不同

    我们遇到了一种奇怪的情况 即我们在 Java 中使用的加密方法会向 openssl 生成不同的输出 尽管它们在配置上看起来相同 使用相同的键和 IV 文本 敏捷的棕色狐狸跳过了懒狗 加密为 Base64 字符串 openssl A8cMRI
  • 比较两个文本文件的最快方法是什么,不将移动的行视为不同

    我有两个文件非常大 每个文件有 50000 行 我需要比较这两个文件并识别更改 然而 问题是如果一条线出现在不同的位置 它不应该显示为不同的 例如 考虑这个文件A txt xxxxx yyyyy zzzzz 文件B txt zzzzz xx
  • 当从服务类中调用时,Spring @Transactional 不适用于带注释的方法

    在下面的代码中 当方法内部 是从内部调用的方法外部 应该在交易范围内 但事实并非如此 但当方法内部 直接从调用我的控制器class 它受到事务的约束 有什么解释吗 这是控制器类 Controller public class MyContr
  • 使用 AES SecretKey 的 Java KeyStore setEntry()

    我目前正在 Java 中开发一个密钥处理类 特别是使用 KeyStore 我正在尝试使用 AES 实例生成 SecretKey 然后使用 setEntry 方法将其放入 KeyStore 中 我已经包含了代码的相关部分 The KS Obj
  • Java 文件上传速度非常慢

    我构建了一个小型服务 它从 Android 设备接收图像并将其保存到 Amazon S3 存储桶中 代码非常简单 但是速度非常慢 事情是这样的 public synchronized static Response postCommentP
  • 如何模拟从抽象类继承的受保护子类方法?

    如何使用 Mockito 或 PowerMock 模拟由子类实现但从抽象超类继承的受保护方法 换句话说 我想在模拟 doSomethingElse 的同时测试 doSomething 方法 抽象超类 public abstract clas
  • Java 中的“Lambdifying”scala 函数

    使用Java和Apache Spark 已用Scala重写 面对旧的API方法 org apache spark rdd JdbcRDD构造函数 其参数为 AbstractFunction1 abstract class AbstractF
  • 很好地处理数据库约束错误

    再一次 它应该很简单 我的任务是在我们的应用程序的域对象中放置一个具有唯一约束的特定字段 这本身并不是一个很大的挑战 我刚刚做了以下事情 public class Location more fields Column unique tru
  • 使用过滤器@ComponentScan所有包的危险

    我现在正在开发一个概念应用程序 我想使用组件扫描来使用特定的自定义 Spring 元注释来获取类路径上任何位置的所有类 我的注释如下所示 Target value ElementType TYPE Retention value Reten
  • 欧洲中部时间 14 日 3 月 30 日星期五 00:00:00 至 日/月/年

    我尝试解析格式日期Fri Mar 30 00 00 00 CET 14至 日 月 年 这是我的代码 SimpleDateFormat formatter new SimpleDateFormat dd MM yyyy System out
  • 如何知道抛出了哪个异常

    我正在对我们的代码库进行审查 有很多这样的陈述 try doSomething catch Exception e 但我想要一种方法来知道 doSomething 抛出了哪个异常 在 doSomething 的实现中没有 throw 语句
  • 考虑在配置中定义“org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder”类型的 bean

    我正在尝试将 jwt 令牌连接到我的项目 但在工作过程中遇到了一些问题 我已按照上述说明进行操作here https auth0 com blog implementing jwt authentication on spring boot
  • 测试弱引用

    在 Java 中测试弱引用的正确方法是什么 我最初的想法是执行以下操作 public class WeakReferenceTest public class Target private String value public Targe
  • 游戏内的java.awt.Robot?

    我正在尝试使用下面的代码来模拟击键 当我打开记事本时 它工作正常 但当我打开我想使用它的游戏时 它没有执行任何操作 所以按键似乎不起作用 我尝试模拟鼠标移动和点击 这些动作确实有效 有谁知道如何解决这个问题 我发现这个问题 如何在游戏中使用
  • spring中如何使用jackson代替JdkSerializationRedisSerializer

    我在我的一个 Java 应用程序中使用 Redis 并且正在序列化要存储在 Redis 中的对象列表 但是 我注意到使用 RedisTemplate 会使用 JdkSerializationRedisSerializer 相反 我想使用 J
  • 如何使用 JSch 将多行命令输出存储到变量中

    所以 我有一段很好的代码 我很难理解 它允许我向我的服务器发送命令 并获得一行响应 该代码有效 但我想从服务器返回多行 主要类是 JSch jSch new JSch MyUserInfo ui new MyUserInfo String
  • Trie 数据结构 - Java [关闭]

    Closed 这个问题不符合堆栈溢出指南 help closed questions 目前不接受答案 是否有任何库或文档 链接提供了在 java 中实现 Trie 数据结构的更多信息 任何帮助都会很棒 Thanks 你可以阅读Java特里树

随机推荐