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

审计日志】,简单的说就是系统需要记录谁,在什么时间,对什么数据,做了什么样的更改!任何一个 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.mzph.cn/news/877594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

整体思想以及取模

前言&#xff1a;一开始由于失误&#xff0c;误以为分数相加取模不能&#xff0c;但是其实是可以取模的 这个题目如果按照一般方法&#xff0c;到达每个节点再进行概率统计&#xff0c;但是不知道为什么只过了百分之十五的测试集 题目地址 附上没过关的代码 #include<bits…

联想闪电鲨移动硬盘文件没删除却消失了怎么办

在日常的数据存储与管理中&#xff0c;移动硬盘作为便携且容量可观的存储设备&#xff0c;深受用户青睐。然而&#xff0c;当您发现联想闪电鲨移动硬盘中的文件突然消失&#xff0c;而您确信并未进行删除操作时&#xff0c;这无疑会令人感到困惑与焦虑。本文旨在为您揭开这一谜…

vue-element-admin——<keep-alive>不符合预期缓存的原因

vue-element-admin——<keep-alive>不符合预期缓存的原因 本文章&#xff0c;以现在中后台开发用的非常多的开源项目vue-element-admin为案例。首先&#xff0c;列出官方文档与缓存<keep-alive>相关的链接&#xff08;请认真阅读&#xff0c;出现缓存<keep-ali…

在IEDA里打包Maven项目记录

之前在网上查找到的方式发现比较繁琐&#xff0c;所以把自己的解决办法记录一下分享给兄弟们 <plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-shade-plugin</artifactId><version>3.2.4</vers…

集合及数据结构第一节————初识集合框架和数据结构

系列文章目录 集合及数据结构第一节————初识集合框架和数据结构 初始集合框架和数据结构 什么是集合框架&#xff1f;集合框架的重要性背后所涉及的数据结构以及算法数据结构的基本概念和术语逻辑结构和物理结构数据类型 文章目录 系列文章目录集合及数据结构第一节——…

文件的读写(标准库函数与系统调用函数),文件描述符的复制

文件描述符 为了解决内核对象在可访问性与安全”性之间的矛盾&#xff0c;Unix系统通过所谓的文件描述符&#xff0c;将位于内核空间中的文件表项间接地提供给运行于用户空间中的程序代码。为了便于管理在系统中运行的各个进程&#xff0c;内核会维护一张存有各进程信息的列表&…

C++智能指针配合STL模板类

代码 #include <unordered_map> #include <set> #include <memory> class ResID { public:using SP std::shared_ptr<ResID>;ResID() default;ResID(const std::string& id, const std::string& type): m_id(id), m_type(type){}public:~Re…

HoloLens 坐标系统 Coordinate systems

Hololens 和 Unity 空间坐标系统-CSDN博客文章浏览阅读79次。这意味着&#xff0c;在 X、Y 或 Z 轴上相距 2 个单位的物体&#xff0c;在混合现实中的渲染效果是相距 2 米。虽然左手坐标和右手坐标是最常见的系统&#xff0c;但 3D 软件中也会使用其他坐标系。例如&#xff0c;…

Veritas NBU8.3.0.2 安装部署环境备份实施介绍(篇一)

1、本次有两台NBU服务器&#xff0c;都是Windows Server 2016 Standard 2、一台作为Master Server角色&#xff0c;另外一台则作为Media Server角色 3、两台服务器均已加入域并关闭防火墙 4、后期备份客户端会有Linux和Windows系统&#xff0c;也会对接VCenter无代理备份虚拟…

xss靶场详解

目录 1.第一题 2.第二题 3.第三题 4.第四题 5.第五题 6.第六题 7.第七题 8.第八题 1.第一题 在源码script标签里边&#xff0c;innerhtml是用于访问或修改 HTML 元素内的 HTML 内容的&#xff0c;这里是访问spaghet这个元素的&#xff0c;并通过括号里面的东西搜索当前…

图搜v1.1.3 - 图库几千张图片再也不怕了

图搜是一款使用先进的自然语言处理技术的相册搜索助手&#xff0c;支持通过简单的描述来搜索相册中的图片&#xff0c;如“草丛中的猫”或“公园里的狗”。这款应用完全免费、无需内购&#xff0c;且完全离线使用&#xff0c;保证了用户数据的安全与私密性。它的高效索引系统确…

法律知识有奖竞答

关于开展线上法律知识竞赛活动的通知 根据字【2024】008号 《关于进一步推动普法贯穿于各行业各领域的倡议》的相关要求&#xff0c;特举办本次线上答题活动。 一、活动时间 2024年08月19日08:00至08月25日24:00 二、活动对象 公司全体成员 三、竞赛规则 1、每人每天拥有1次…

Jenkins UI与接口自动化测试持续集成实战

篇幅较长&#xff0c;要耐心阅读哦~ 基础知识简要回顾 持续集成、持续交付的好处与产生的必然性Jenkins服务的搭建方法Jenkins节点管理与用户权限Jenkins插件Jenkins父子多任务关联运行Jenkins报警机制 目录 SeleniumUI自动化测试持续集成演练接口自动化测试持续集成演练 …

vue+echarts:echarts地图页面跳转

在setOption的平级写点击事件&#xff0c;给chart添加click监听 getmapChart.setOption({......})//和数据对应即可 //点击区域实现页面跳转getmapChart.on(click,function(params){// console.log(params);switch(params.name){case "xxxx":top.location.href"…

JUC阻塞队列(四):DelayQueue

1、DelayQueue介绍 DelayQueue 是一个延迟队列&#xff0c;生产者写入一个数据&#xff0c;这个数据具有被直接消费的延迟时间&#xff0c; 让数据具有延迟的特性。 DelayQueue底层也是基于二叉堆来实现的&#xff0c;DelayQueue本就是基于PriorityBQueue 实现的。 二叉堆结构每…

Kubernetes Pod入门

在 Kubernetes 中&#xff0c;一个重要的概念就是 Pod(豆英)&#xff0c;Kubernetes 并不是直接管理容器他的最小管理单元叫做 Pod。的&#xff0c; 在 Docker 的应用中&#xff0c;我们把一个应用程序封装在一个镜像中&#xff0c;之后启动这个镜像并映射一个宿主机端…

x-cmd pkg | dua - 一个可以方便地了解给定目录的磁盘空间使用情况的工具

目录 简介用户首次快速实验指南技术特点竞品和相关项目进一步阅读 简介 dua 是 Disk Usage Analyzer 的简写&#xff0c;该工具可以快速查看给定目录的磁盘空间使用情况。 对于想要深入了解磁盘空间使用情况并有效管理存储的用户来说&#xff0c;Dua 是一个很有价值的工具。通…

华硕飞行堡垒键盘全部失灵【除电源键】

华硕飞行堡垒FX53VD键盘全部失灵【除电源键】 前言一、故障排查二、发现问题三、使用方法总结 前言 版本型号&#xff1a; 型号 ASUS FX53VD&#xff08;华硕-飞行堡垒&#xff09; 板号&#xff1a;GL553VD 故障情况描述&#xff1a; 键盘无法使用&#xff0c;键盘除开机键外…

轮式自主移动机器人的研究发展与ROS环境搭建

前言&#xff1a; 在轮式自主移动机器人的研发过程中&#xff0c;编程技术的重要性不言而喻。编程不仅涉及到机器人各种功能模块的协调运作&#xff0c;还决定了机器人能否实现复杂的行为决策和控制。首先&#xff0c;编程技术为机器人提供了“大脑”&#xff0c;从而使其能够感…

暑假请停止躺平!0门槛赚22万奖金!

在这个数字化时代&#xff0c;数据成为推动社会进步的重要力量。您是否想运用手中的数据&#xff0c;为治理与环保领域贡献力量&#xff1f;现在&#xff0c;机会就在眼前&#xff01; 2024年厦门市大数据创新应用大赛正在火热进行中&#xff0c;我们诚邀全国高等院校在校学生…