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

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

相关文章

Android 悬浮窗开发示例((动态权限请求 | 前台服务和通知 | 悬浮窗创建 )

《Android悬浮窗开发示例((动态权限请求|前台服务和通知|悬浮窗创建)》本文介绍了Android悬浮窗的实现效果,包括动态权限请求、前台服务和通知的使用,悬浮窗权限需要动态申请并引导... 目录一、悬浮窗 动态权限请求1、动态请求权限2、悬浮窗权限说明3、检查动态权限4、申请动态权限5、权限设置完毕后

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

TP-Link PDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务

《TP-LinkPDDNS服将于务6月30日正式停运:用户需转向第三方DDNS服务》近期,路由器制造巨头普联(TP-Link)在用户群体中引发了一系列重要变动,上个月,公司发出了一则通知,明确要求所... 路由器厂商普联(TP-Link)上个月发布公告要求所有用户必须完成实名认证后才能继续使用普联提供的 D

Spring Boot整合log4j2日志配置的详细教程

《SpringBoot整合log4j2日志配置的详细教程》:本文主要介绍SpringBoot项目中整合Log4j2日志框架的步骤和配置,包括常用日志框架的比较、配置参数介绍、Log4j2配置详解... 目录前言一、常用日志框架二、配置参数介绍1. 日志级别2. 输出形式3. 日志格式3.1 PatternL

Spring排序机制之接口与注解的使用方法

《Spring排序机制之接口与注解的使用方法》本文介绍了Spring中多种排序机制,包括Ordered接口、PriorityOrdered接口、@Order注解和@Priority注解,提供了详细示例... 目录一、Spring 排序的需求场景二、Spring 中的排序机制1、Ordered 接口2、Pri

Idea实现接口的方法上无法添加@Override注解的解决方案

《Idea实现接口的方法上无法添加@Override注解的解决方案》文章介绍了在IDEA中实现接口方法时无法添加@Override注解的问题及其解决方法,主要步骤包括更改项目结构中的Languagel... 目录Idea实现接China编程口的方法上无法添加@javascriptOverride注解错误原因解决方

轻松上手MYSQL之JSON函数实现高效数据查询与操作

《轻松上手MYSQL之JSON函数实现高效数据查询与操作》:本文主要介绍轻松上手MYSQL之JSON函数实现高效数据查询与操作的相关资料,MySQL提供了多个JSON函数,用于处理和查询JSON数... 目录一、jsON_EXTRACT 提取指定数据二、JSON_UNQUOTE 取消双引号三、JSON_KE

Go Mongox轻松实现MongoDB的时间字段自动填充

《GoMongox轻松实现MongoDB的时间字段自动填充》这篇文章主要为大家详细介绍了Go语言如何使用mongox库,在插入和更新数据时自动填充时间字段,从而提升开发效率并减少重复代码,需要的可以... 目录前言时间字段填充规则Mongox 的安装使用 Mongox 进行插入操作使用 Mongox 进行更

Java中基于注解的代码生成工具MapStruct映射使用详解

《Java中基于注解的代码生成工具MapStruct映射使用详解》MapStruct作为一个基于注解的代码生成工具,为我们提供了一种更加优雅、高效的解决方案,本文主要为大家介绍了它的具体使用,感兴趣... 目录介绍优缺点优点缺点核心注解及详细使用语法说明@Mapper@Mapping@Mappings@Co

开启mysql的binlog日志步骤详解

《开启mysql的binlog日志步骤详解》:本文主要介绍MySQL5.7版本中二进制日志(bin_log)的配置和使用,文中通过图文及代码介绍的非常详细,需要的朋友可以参考下... 目录1.查看是否开启bin_log2.数据库会把日志放进logs目录中3.查看log日志总结 mysql版本5.71.查看