目录

  • 一、前言
  • 二、实战
    • 1、设计用户操作日志表: sys_oper_log
    • 2、引入依赖
    • 3、自定义用户操作日志注解
    • 4、自定义用户操作日志切面
    • 5、MyLog注解的使用
    • 6、最终效果
  • 三、总结

一、前言

本文主要介绍通过Aop记录用户操作日志,这也是目前比较常用的用法,由于水平有限,所以可能存在错漏之处,望指正。

二、实战

1、设计用户操作日志表: sys_oper_log


对应实体类为SysOperLog.java

import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.util.Date;/*** <p>* 操作日志记录* </p>*/
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class SysOperLog implements Serializable {private static final long serialVersionUID = 1L;@TableId(value = "id", type = IdType.AUTO)@ApiModelProperty("主键Id")private Integer id;@ApiModelProperty("模块标题")private String title;@ApiModelProperty("参数")private String optParam;@ApiModelProperty("业务类型(0其它 1新增 2修改 3删除)")private Integer businessType;@ApiModelProperty("路径名称")private String uri;@ApiModelProperty("操作状态(0正常 1异常)")private Integer status;@ApiModelProperty("错误消息")private String errorMsg;@ApiModelProperty("操作时间")private Date operTime;
}

2、引入依赖

<dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>2.0.9</version>
</dependency>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>

3、自定义用户操作日志注解

MyLog.java

import java.lang.annotation.*;@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyLog {// 自定义模块名,eg:登录String title() default "";// 方法传入的参数String optParam() default "";// 操作类型,eg:INSERT, UPDATE...BusinessType businessType() default BusinessType.OTHER;
}

BusinessType.java — 操作类型枚举类

public enum BusinessType {// 其它OTHER,// 查找SELECT,// 新增INSERT,// 修改UPDATE,// 删除DELETE,
}

4、自定义用户操作日志切面

LogAspect.java

import com.alibaba.fastjson.JSONObject;
import iot.sixiang.license.entity.SysOperLog;
import iot.sixiang.license.handler.IotLicenseException;
import iot.sixiang.license.jwt.UserUtils;
import iot.sixiang.license.service.SysOperLogService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;import java.lang.reflect.Method;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;@Aspect
@Component
@Slf4j
public class LogAspect {/*** 该Service及其实现类相关代码请自行实现,只是一个简单的插入数据库操作*/@Autowiredprivate SysOperLogService sysOperLogService;/*** @annotation(MyLog类的路径) 在idea中,右键自定义的MyLog类-> 点击Copy Reference*/@Pointcut("@annotation(xxx.xxx.xxx.MyLog)")public void logPointCut() {log.info("------>配置织入点");}/*** 处理完请求后执行** @param joinPoint 切点*/@AfterReturning(pointcut = "logPointCut()")public void doAfterReturning(JoinPoint joinPoint) {handleLog(joinPoint, null);}/*** 拦截异常操作** @param joinPoint 切点* @param e         异常*/@AfterThrowing(value = "logPointCut()", throwing = "e")public void doAfterThrowing(JoinPoint joinPoint, Exception e) {handleLog(joinPoint, e);}private void handleLog(final JoinPoint joinPoint, final Exception e) {// 获得MyLog注解MyLog controllerLog = getAnnotationLog(joinPoint);if (controllerLog == null) {return;}SysOperLog operLog = new SysOperLog();// 操作状态(0正常 1异常)operLog.setStatus(0);// 操作时间operLog.setOperTime(new Date());if (e != null) {operLog.setStatus(1);// IotLicenseException为本系统自定义的异常类,读者若要获取异常信息,请根据自身情况变通operLog.setErrorMsg(StringUtils.substring(((IotLicenseException) e).getMsg(), 0, 2000));}// UserUtils.getUri();获取方法上的路径 如:/login,本文实现方法如下:// 1、在拦截器中 String uri = request.getRequestURI();// 2、用ThreadLocal存放uri,UserUtils.setUri(uri);// 3、UserUtils.getUri();String uri = UserUtils.getUri();operLog.setUri(uri);// 处理注解上的参数getControllerMethodDescription(joinPoint, controllerLog, operLog);// 保存数据库sysOperLogService.addOperlog(operLog.getTitle(), operLog.getBusinessType(), operLog.getUri(), operLog.getStatus(), operLog.getOptParam(), operLog.getErrorMsg(), operLog.getOperTime());}/*** 是否存在注解,如果存在就获取,不存在则返回null* @param joinPoint* @return*/private MyLog getAnnotationLog(JoinPoint joinPoint) {Signature signature = joinPoint.getSignature();MethodSignature methodSignature = (MethodSignature) signature;Method method = methodSignature.getMethod();if (method != null) {return method.getAnnotation(MyLog.class);}return null;}/*** 获取Controller层上MyLog注解中对方法的描述信息* @param joinPoint 切点* @param myLog 自定义的注解* @param operLog 操作日志实体类*/private void getControllerMethodDescription(JoinPoint joinPoint, MyLog myLog, SysOperLog operLog) {// 设置业务类型(0其它 1新增 2修改 3删除)operLog.setBusinessType(myLog.businessType().ordinal());// 设置模块标题,eg:登录operLog.setTitle(myLog.title());// 对方法上的参数进行处理,处理完:userName=xxx,password=xxxString optParam = getAnnotationValue(joinPoint, myLog.optParam());operLog.setOptParam(optParam);}/*** 对方法上的参数进行处理* @param joinPoint* @param name* @return*/private String getAnnotationValue(JoinPoint joinPoint, String name) {String paramName = name;// 获取方法中所有的参数Map<String, Object> params = getParams(joinPoint);// 参数是否是动态的:#{paramName}if (paramName.matches("^#\\{\\D*\\}")) {// 获取参数名,去掉#{ }paramName = paramName.replace("#{", "").replace("}", "");// 是否是复杂的参数类型:对象.参数名if (paramName.contains(".")) {String[] split = paramName.split("\\.");// 获取方法中对象的内容Object object = getValue(params, split[0]);// 转换为JsonObjectJSONObject jsonObject = (JSONObject) JSONObject.toJSON(object);// 获取值Object o = jsonObject.get(split[1]);return String.valueOf(o);} else {// 简单的动态参数直接返回StringBuilder str = new StringBuilder();String[] paraNames = paramName.split(",");for (String paraName : paraNames) {String val = String.valueOf(getValue(params, paraName));// 组装成 userName=xxx,password=xxx,str.append(paraName).append("=").append(val).append(",");}// 去掉末尾的,if (str.toString().endsWith(",")) {String substring = str.substring(0, str.length() - 1);return substring;} else {return str.toString();}}}// 非动态参数直接返回return name;}/*** 获取方法上的所有参数,返回Map类型, eg: 键:"userName",值:xxx  键:"password",值:xxx* @param joinPoint* @return*/public Map<String, Object> getParams(JoinPoint joinPoint) {Map<String, Object> params = new HashMap<>(8);// 通过切点获取方法所有参数值["zhangsan", "123456"]Object[] args = joinPoint.getArgs();// 通过切点获取方法所有参数名 eg:["userName", "password"]MethodSignature signature = (MethodSignature) joinPoint.getSignature();String[] names = signature.getParameterNames();for (int i = 0; i < args.length; i++) {params.put(names[i], args[i]);}return params;}/*** 从map中获取键为paramName的值,不存在放回null* @param map* @param paramName* @return*/private Object getValue(Map<String, Object> map, String paramName) {for (Map.Entry<String, Object> entry : map.entrySet()) {if (entry.getKey().equals(paramName)) {return entry.getValue();}}return null;}
}

5、MyLog注解的使用

@GetMapping("login")
@MyLog(title = "登录", optParam = "#{userName},#{password}", businessType = BusinessType.OTHER)
public DataResult login(@RequestParam("userName") String userName, @RequestParam("password") String password) {...
}

6、最终效果

三、总结

用户操作日志是AOP最常见的一种业务场景,这里只是简单记录了少量信息,如果需要更详细的信息,就需要读者自行去组装和改造。

Spring Boot + Aop 记录用户操作日志相关推荐

  1. Spring Boot AOP记录用户操作日志

    一.引入必要依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId> ...

  2. ssm 项目记录用户操作日志和异常日志

    ssm 项目记录用户操作日志和异常日志 参考文章: (1)ssm 项目记录用户操作日志和异常日志 (2)https://www.cnblogs.com/mei-m/p/10231792.html (3 ...

  3. 中操作日志文件记录的是什么_SpringBoot+AOP实现用户操作日志的记录

    前言: 任何一个项目都会有一个用户操作日志(也叫行为日志)的模块,它主要用来记录某个用户做了某个操作,当出现操作失败时,通过日志就可以快速的查找是哪个用户在哪个模块出现了错误,以便于开发人员快速定位问 ...

  4. Linux记录用户操作日志

    前言 我这里计划用SLS做服务器日志审计,安装Logtail后通过 [分隔符-文本] 的方式,采集服务器各种相关日志,大概需要以下内容(目录可能是错的,这个是做等保的评测的兄弟发过来的) /var/l ...

  5. Spring Boot Aop结合Swagger记录用户操作日志

    1.引入依赖 <dependency><groupId>io.springfox</groupId><artifactId>springfox-swag ...

  6. spring boot aop 记录方法执行时间

    了性能调优,需要先统计出来每个方法的执行时间,直接在方法前后log输出太麻烦,可以用AOP来加入时间统计 添加依赖 <dependency> <groupId>org.spri ...

  7. Spring Boot——基于AOP的HTTP操作日志解决方案

    解决方案 package com.hailiu.web.aop;import com.fasterxml.jackson.databind.ObjectMapper; import com.haili ...

  8. 使用Spring AOP自定义注解方式实现用户操作日志记录

    1,开发环境 操作系统:Windows 7 JDK:1.8.0_161 Eclipse:Mars.2 Release (4.5.2) 2,自定义注解类UserLog @Target({ElementT ...

  9. spring boot项目怎么记录用户操作行为和登录时间_6 个 Github 项目拿下 Spring Boot

    经常浏览技术社区.技术公众号的读者会有一个感受,那么就是 Spring Boot 相关的文章和相关咨询越来越多.包括小逛和技术公众号的博主交流,他们也发现推送 Spring Boot 相关的文章阅读量 ...

最新文章

  1. 线程安全问题产生的原因
  2. 数据结构与算法专题——第十题 输入法跳不过的坎-伸展树
  3. mysql fpmmm_zabbix配fpmmm(mpm)数据传送不了问题解决
  4. Python数据挖掘与机器学习技术入门实战
  5. Vue3+Vant+Vue-cli+Restful api实战—图书商城移动端
  6. SVN工作笔记006---解决TortoiseSVN中out of date问题的一个方法
  7. JavaScript异常处理
  8. 【转】利用Eclipse编辑中文资源文件(application_zh_CN.properties )
  9. 微型计算机是一种将CPU,Bwnhlq计算机一级考试选择题题库之微型计算机基础题及答案(XX年最新版)1.doc...
  10. ArcGIS for Android地图控件的5大常见操作
  11. (一)HBase基础介绍
  12. Masonry 布局 cell 高度适应的一种方案(实现类似朋友圈简单布局)
  13. 几种典型的智慧农业物联网解决方案
  14. java 正则表达式 例子_Java正则表达式例子汇总
  15. 实时查看Starlink在轨卫星、地面站数目和分布情况的有趣网站
  16. c#中value是什么意思
  17. nacos运行报jar的错Failed to get nested archive for entry BOOT-INF/lib/XXX.jar
  18. NOI2006:金明的预算方案
  19. 全球圣诞节离不开义乌!义乌购发布圣诞用品搜索热度指数;阿迪达斯将成为Jr. NBA中国官方市场合作伙伴 | 美通社头条...
  20. 很合理,但是有病的奇怪网站合集

热门文章

  1. EFI和UEFI的区别
  2. 谷歌裁员细节曝光:开源主管被裁,61岁程序员在线求职,有人60天找不到工作就被遣返
  3. 华为王俊:Weink将持续携手伙伴做行业开拓
  4. numpy np.random.random()函数和random.uniform()函数
  5. 如何在Windows XP上飆速Android intel模擬器加速器(HAXM)
  6. Elasticsearch 整合springboot-Elasticsearch文章二
  7. 为生活加点料,用python帮你选择双色球号码
  8. 埃拉托斯特尼筛法算法复杂度(n*lnlnn)的证明([欧拉数学]素数倒数之和)
  9. 计算机成瘾综合症的表现有,4个心理表现表明你犯了网瘾综合症
  10. 打开网页出现错误提示out of memory