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

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

相关文章

PHP轻松处理千万行数据的方法详解

《PHP轻松处理千万行数据的方法详解》说到处理大数据集,PHP通常不是第一个想到的语言,但如果你曾经需要处理数百万行数据而不让服务器崩溃或内存耗尽,你就会知道PHP用对了工具有多强大,下面小编就... 目录问题的本质php 中的数据流处理:为什么必不可少生成器:内存高效的迭代方式流量控制:避免系统过载一次性

SpringBoot日志级别与日志分组详解

《SpringBoot日志级别与日志分组详解》文章介绍了日志级别(ALL至OFF)及其作用,说明SpringBoot默认日志级别为INFO,可通过application.properties调整全局或... 目录日志级别1、级别内容2、调整日志级别调整默认日志级别调整指定类的日志级别项目开发过程中,利用日志

SpringBoot 获取请求参数的常用注解及用法

《SpringBoot获取请求参数的常用注解及用法》SpringBoot通过@RequestParam、@PathVariable等注解支持从HTTP请求中获取参数,涵盖查询、路径、请求体、头、C... 目录SpringBoot 提供了多种注解来方便地从 HTTP 请求中获取参数以下是主要的注解及其用法:1

深度解析Java @Serial 注解及常见错误案例

《深度解析Java@Serial注解及常见错误案例》Java14引入@Serial注解,用于编译时校验序列化成员,替代传统方式解决运行时错误,适用于Serializable类的方法/字段,需注意签... 目录Java @Serial 注解深度解析1. 注解本质2. 核心作用(1) 主要用途(2) 适用位置3

sysmain服务可以禁用吗? 电脑sysmain服务关闭后的影响与操作指南

《sysmain服务可以禁用吗?电脑sysmain服务关闭后的影响与操作指南》在Windows系统中,SysMain服务(原名Superfetch)作为一个旨在提升系统性能的关键组件,一直备受用户关... 在使用 Windows 系统时,有时候真有点像在「开盲盒」。全新安装系统后的「默认设置」,往往并不尽编

Python 基于http.server模块实现简单http服务的代码举例

《Python基于http.server模块实现简单http服务的代码举例》Pythonhttp.server模块通过继承BaseHTTPRequestHandler处理HTTP请求,使用Threa... 目录测试环境代码实现相关介绍模块简介类及相关函数简介参考链接测试环境win11专业版python

深度剖析SpringBoot日志性能提升的原因与解决

《深度剖析SpringBoot日志性能提升的原因与解决》日志记录本该是辅助工具,却为何成了性能瓶颈,SpringBoot如何用代码彻底破解日志导致的高延迟问题,感兴趣的小伙伴可以跟随小编一起学习一下... 目录前言第一章:日志性能陷阱的底层原理1.1 日志级别的“双刃剑”效应1.2 同步日志的“吞吐量杀手”

Nginx中配置使用非默认80端口进行服务的完整指南

《Nginx中配置使用非默认80端口进行服务的完整指南》在实际生产环境中,我们经常需要将Nginx配置在其他端口上运行,本文将详细介绍如何在Nginx中配置使用非默认端口进行服务,希望对大家有所帮助... 目录一、为什么需要使用非默认端口二、配置Nginx使用非默认端口的基本方法2.1 修改listen指令

Java利用@SneakyThrows注解提升异常处理效率详解

《Java利用@SneakyThrows注解提升异常处理效率详解》这篇文章将深度剖析@SneakyThrows的原理,用法,适用场景以及隐藏的陷阱,看看它如何让Java异常处理效率飙升50%,感兴趣的... 目录前言一、检查型异常的“诅咒”:为什么Java开发者讨厌它1.1 检查型异常的痛点1.2 为什么说

SysMain服务可以关吗? 解决SysMain服务导致的高CPU使用率问题

《SysMain服务可以关吗?解决SysMain服务导致的高CPU使用率问题》SysMain服务是超级预读取,该服务会记录您打开应用程序的模式,并预先将它们加载到内存中以节省时间,但它可能占用大量... 在使用电脑的过程中,CPU使用率居高不下是许多用户都遇到过的问题,其中名为SysMain的服务往往是罪魁