一个注解轻松搞定审计日志服务!

2024-08-22 00:12

本文主要是介绍一个注解轻松搞定审计日志服务!,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

审计日志】,简单的说就是系统需要记录谁,在什么时间,对什么数据,做了什么样的更改!任何一个 IT 系统,如果要过审,这项任务基本上也是必审项!

实现【审计日志】这个需求,我们有一个很好的技术解决方案,就是使用 Spring 的切面编程。

  • 先创建审计日志表

图片

然后编写一个注解类

图片

接着编写一个代理类

@Component
@Aspect
public class SystemAuditLogAspect {@Autowiredprivate SystemAuditLogService systemAuditLogService;/*** 定义切入点,切入所有标注此注解的类和方法*/@Pointcut("@within(com.example.demo.core.annotation.SystemAuditLog)|| @annotation(com.example.demo.core.annotation.SystemAuditLog)")public void methodAspect() {}/*** 方法调用前拦截*/@Before("methodAspect()")public void before(){System.out.println("SystemAuditLog代理 -> 调用方法执行之前......");}/*** 方法调用后拦截*/@After("methodAspect()")public void after(){System.out.println("SystemAuditLog代理 -> 调用方法执行之后......");}/*** 调用方法结束拦截*/@AfterReturning(value = "methodAspect()")public void afterReturning(JoinPoint joinPoint) throws Exception {System.out.println("SystemAuditLog代理 -> 调用方法结束拦截......");//封装数据AuditLog entity = warpAuditLog(joinPoint);entity.setResult(0);//插入到数据库systemAuditLogService.add(entity);}/*** 抛出异常拦截*/@AfterThrowing(value="methodAspect()", throwing="ex")public void afterThrowing(JoinPoint joinPoint, Exception ex) throws Exception {System.out.println("SystemAuditLog代理 -> 抛出异常拦截......");//封装数据AuditLog entity = warpAuditLog(joinPoint);entity.setResult(1);//封装错误信息entity.setExMsg(ex.getMessage());//插入到数据库systemAuditLogService.add(entity);}/*** 封装插入实体* @param joinPoint* @return* @throws Exception*/private AuditLog warpAuditLog(JoinPoint joinPoint) throws Exception {//获取请求上下文HttpServletRequest request = getHttpServletRequest();//获取注解上的参数值SystemAuditLog systemAuditLog = getServiceMethodDescription(joinPoint);//获取请求参数Object requestObj = getServiceMethodParams(joinPoint);//封装数据AuditLog auditLog = new AuditLog();auditLog.setId(SnowflakeIdWorker.getInstance().nextId());//从请求上下文对象获取相应的数据if(Objects.nonNull(request)){auditLog.setUserAgent(request.getHeader("User-Agent"));//获取登录时的ip地址auditLog.setIpAddress(IpAddressUtil.getIpAddress(request));//调用外部接口,获取IP所在地auditLog.setIpAddressName(IpAddressUtil.getLoginAddress(auditLog.getIpAddress()));}//封装操作的表和描述if(Objects.nonNull(systemAuditLog)){auditLog.setTableName(systemAuditLog.tableName());auditLog.setOperateDesc(systemAuditLog.description());}//封装请求参数auditLog.setRequestParam(JSON.toJSONString(requestObj));//封装请求人if(Objects.nonNull(requestObj) && requestObj instanceof BaseRequest){auditLog.setOperateUserId(((BaseRequest) requestObj).getLoginUserId());auditLog.setOperateUserName(((BaseRequest) requestObj).getLoginUserName());}auditLog.setOperateTime(new Date());return auditLog;}/*** 获取当前的request* 这里如果报空指针异常是因为单独使用spring获取request* 需要在配置文件里添加监听** 如果是spring项目,通过下面方式注入* <listener>* <listener-class>* org.springframework.web.context.request.RequestContextListener* </listener-class>* </listener>** 如果是springboot项目,在配置类里面,通过下面方式注入* @Bean* public RequestContextListener requestContextListener(){*     return new RequestContextListener();* }* @return*/private HttpServletRequest getHttpServletRequest(){RequestAttributes ra = RequestContextHolder.getRequestAttributes();ServletRequestAttributes sra = (ServletRequestAttributes)ra;HttpServletRequest request = sra.getRequest();return request;}/*** 获取请求对象* @param joinPoint* @return* @throws Exception*/private Object getServiceMethodParams(JoinPoint joinPoint) {Object[] arguments = joinPoint.getArgs();if(Objects.nonNull(arguments) && arguments.length > 0){return arguments[0];}return null;}/*** 获取自定义注解里的参数* @param joinPoint* @return 返回注解里面的日志描述* @throws Exception*/private SystemAuditLog getServiceMethodDescription(JoinPoint joinPoint) throws Exception {//类名String targetName = joinPoint.getTarget().getClass().getName();//方法名String methodName = joinPoint.getSignature().getName();//参数Object[] arguments = joinPoint.getArgs();//通过反射获取示例对象Class targetClass = Class.forName(targetName);//通过实例对象方法数组Method[] methods = targetClass.getMethods();for(Method method : methods) {//判断方法名是不是一样if(method.getName().equals(methodName)) {//对比参数数组的长度Class[] clazzs = method.getParameterTypes();if(clazzs.length == arguments.length) {//获取注解里的日志信息return method.getAnnotation(SystemAuditLog.class);}}}return null;}
}
  • 最后,只需要在对应的接口或者方法上添加审计日志注解即可

图片

  • 相关的实体类

@Data
public class AuditLog {/*** 审计日志,主键ID*/private Long id;/*** 操作的表名,多个用逗号隔开*/private String tableName;/*** 操作描述*/private String operateDesc;/*** 请求参数*/private String requestParam;/*** 执行结果,0:成功,1:失败*/private Integer result;/*** 异常信息*/private String exMsg;/*** 请求代理信息*/private String userAgent;/*** 操作时设备IP*/private String ipAddress;/*** 操作时设备IP所在地址*/private String ipAddressName;/*** 操作时间*/private Date operateTime;/*** 操作人ID*/private String operateUserId;/*** 操作人*/private String operateUserName;
}
public class BaseRequest implements Serializable {/*** 请求token*/private String token;/*** 登录人ID*/private String loginUserId;/*** 登录人姓名*/private String loginUserName;public String getToken() {return token;}public void setToken(String token) {this.token = token;}public String getLoginUserId() {return loginUserId;}public void setLoginUserId(String loginUserId) {this.loginUserId = loginUserId;}public String getLoginUserName() {return loginUserName;}public void setLoginUserName(String loginUserName) {this.loginUserName = loginUserName;}
}
@Data
public class UserLoginDTO extends BaseRequest {/*** 用户名*/private String userName;/*** 密码*/private String password;
}

整个程序的实现过程,主要使用了 Spring AOP 特性,对特定方法进行前、后拦截,从而实现业务方的需求。

最后说一句(求关注!别白嫖!)

如果这篇文章对您有所帮助,或者有所启发的话,求一键三连:点赞、转发、在看。

关注公众号:woniuxgg,在公众号中回复:笔记  就可以获得蜗牛为你精心准备的java实战语雀笔记,回复面试、开发手册、有超赞的粉丝福利!

这篇关于一个注解轻松搞定审计日志服务!的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1094682

相关文章

怎样通过分析GC日志来定位Java进程的内存问题

《怎样通过分析GC日志来定位Java进程的内存问题》:本文主要介绍怎样通过分析GC日志来定位Java进程的内存问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、GC 日志基础配置1. 启用详细 GC 日志2. 不同收集器的日志格式二、关键指标与分析维度1.

解读GC日志中的各项指标用法

《解读GC日志中的各项指标用法》:本文主要介绍GC日志中的各项指标用法,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录一、基础 GC 日志格式(以 G1 为例)1. Minor GC 日志2. Full GC 日志二、关键指标解析1. GC 类型与触发原因2. 堆

Linux中SSH服务配置的全面指南

《Linux中SSH服务配置的全面指南》作为网络安全工程师,SSH(SecureShell)服务的安全配置是我们日常工作中不可忽视的重要环节,本文将从基础配置到高级安全加固,全面解析SSH服务的各项参... 目录概述基础配置详解端口与监听设置主机密钥配置认证机制强化禁用密码认证禁止root直接登录实现双因素

java向微信服务号发送消息的完整步骤实例

《java向微信服务号发送消息的完整步骤实例》:本文主要介绍java向微信服务号发送消息的相关资料,包括申请测试号获取appID/appsecret、关注公众号获取openID、配置消息模板及代码... 目录步骤1. 申请测试系统2. 公众号账号信息3. 关注测试号二维码4. 消息模板接口5. Java测试

Spring如何使用注解@DependsOn控制Bean加载顺序

《Spring如何使用注解@DependsOn控制Bean加载顺序》:本文主要介绍Spring如何使用注解@DependsOn控制Bean加载顺序,具有很好的参考价值,希望对大家有所帮助,如有错误... 目录1.javascript 前言2. 代码实现总结1. 前言默认情况下,Spring加载Bean的顺

Spring @Scheduled注解及工作原理

《Spring@Scheduled注解及工作原理》Spring的@Scheduled注解用于标记定时任务,无需额外库,需配置@EnableScheduling,设置fixedRate、fixedDe... 目录1.@Scheduled注解定义2.配置 @Scheduled2.1 开启定时任务支持2.2 创建

MySQL 打开binlog日志的方法及注意事项

《MySQL打开binlog日志的方法及注意事项》本文给大家介绍MySQL打开binlog日志的方法及注意事项,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要... 目录一、默认状态二、如何检查 binlog 状态三、如何开启 binlog3.1 临时开启(重启后失效)

mapstruct中的@Mapper注解的基本用法

《mapstruct中的@Mapper注解的基本用法》在MapStruct中,@Mapper注解是核心注解之一,用于标记一个接口或抽象类为MapStruct的映射器(Mapper),本文给大家介绍ma... 目录1. 基本用法2. 常用属性3. 高级用法4. 注意事项5. 总结6. 编译异常处理在MapSt

SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志

《SpringBoot项目配置logback-spring.xml屏蔽特定路径的日志》在SpringBoot项目中,使用logback-spring.xml配置屏蔽特定路径的日志有两种常用方式,文中的... 目录方案一:基础配置(直接关闭目标路径日志)方案二:结合 Spring Profile 按环境屏蔽关

SpringBoot服务获取Pod当前IP的两种方案

《SpringBoot服务获取Pod当前IP的两种方案》在Kubernetes集群中,SpringBoot服务获取Pod当前IP的方案主要有两种,通过环境变量注入或通过Java代码动态获取网络接口IP... 目录方案一:通过 Kubernetes Downward API 注入环境变量原理步骤方案二:通过