Spring Boot利用AOP获取用户操作实现日志记录

2022-11-20 15:04:17 浏览数 (1)

思路总结:

需要在日志记录的方法中添加一个自定义注解,再去实现一个日志AOP类,AOP类把自定义注解设置为切点,当系统执行某一个添加了自定义注解的方法时,AOP会自动获取该方法名称以及用户信息实现日志记录。

1. 引入依赖

代码语言:javascript复制
    <dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    </dependency>
    
    <!-- aop依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    <!-- mysql驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>5.1.41</version>
    </dependency>
    
    <!-- druid数据源驱动 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid-spring-boot-starter</artifactId>
        <version>1.1.10</version>
    </dependency>

    <!-- UserAgent -->
    <dependency>
        <groupId>eu.bitwalker</groupId>
        <artifactId>UserAgentUtils</artifactId>
        <version>1.21</version>
    </dependency>
    
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

2. 自定义注解

定义一个方法级别的@Log注解,用于标注需要监控的方法:

代码语言:javascript复制
    @Target(ElementType.METHOD)
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Log {
        String value() default "";
    }

3. 创建库表

在数据库中创建一张sys_log表,用于保存用户的操作日志

代码语言:javascript复制
    -- ----------------------------
    -- Table structure for sys_log
    -- ----------------------------
    DROP TABLE IF EXISTS `sys_log`;
    CREATE TABLE `sys_log` (
      `id` int NOT NULL AUTO_INCREMENT,
      `user_name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
      `operation` varchar(255) DEFAULT NULL,
      `time` decimal(10,0) DEFAULT NULL,
      `method` varchar(255) DEFAULT NULL,
      `params` varchar(9999) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci DEFAULT NULL,
      `ip` varchar(255) DEFAULT NULL,
      `browser` varchar(255) DEFAULT NULL,
      `system` varchar(255) DEFAULT NULL,
      `create_time` datetime DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=874 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
    
    SET FOREIGN_KEY_CHECKS = 1;

4. 创建model类

实体:

代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/6/22
     * @email apple_dzy@163.com
     */
    public class SysLog implements Serializable {
    
        private Integer id;
        private String userName;
        private String operation;
        private Integer time;
        private String method;
        private String params;
        private String ip;
        private String browser;
        private String system;
        private Date createTime;

        getter and setter ......
 
    }

5. 创建dao和service

mapper:

代码语言:javascript复制
    <select id="selectLog" resultMap="logMap">
        select
        *
        from sys_log
        <where>
            <if test="userName != null and userName != ''">
                user_name like concat('%',#{userName},'%')
            </if>
        </where>
        order by id desc
    </select>

dao:

代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/6/22 2:59 下午
     * @email apple_dzy@163.com
     */
    @Mapper
    public interface SysLogMapper extends BaseMapper<SysLog> {
    
        List<SysLog> selectLog(Pagination page, @Param("userName")String userName);
    
    }

service:

代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/6/22 2:59 下午
     * @email apple_dzy@163.com
     */
    @Service
    public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements SysLogService {
    
        @Override
        public Page<SysLog> queryLog(Page page, String userName) {
            return page.setRecords(baseMapper.selectLog(page,userName));
        }
    
    
    }
代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/6/22 3:02 下午
     * @email apple_dzy@163.com
     */
    public interface SysLogService extends IService<SysLog> {
    
        Page<SysLog> queryLog(Page page, String userName);
    
    }

6. 配置Aspect

代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/6/22 3:06 下午
     * @email apple_dzy@163.com
     */
    @Aspect
    @Component
    public class LogAspect {
    
        @Autowired
        private SysLogService sysLogService;
    
        @Pointcut("@annotation(com.initial.annotation.Log)")
        public void pointcut() {
        }
    
        @Around("pointcut()")
        public Object around(ProceedingJoinPoint point) {
            Object result = null;
            long beginTime = System.currentTimeMillis();
            try {
                // 执行方法
                result = point.proceed();
            } catch (Throwable e) {
                e.printStackTrace();
            }
            // 执行时长(毫秒)
            long time = System.currentTimeMillis() - beginTime;
            // 保存日志
            saveLog(point, time);
            return result;
        }
    
        private void saveLog(ProceedingJoinPoint joinPoint, long time) {
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
            SysLog sysLog = new SysLog();
            Log logAnnotation = method.getAnnotation(Log.class);
            if (logAnnotation != null) {
                // 注解上的描述
                sysLog.setOperation(logAnnotation.value());
            }
            // 请求的方法名
            String className = joinPoint.getTarget().getClass().getName();
            String methodName = signature.getName();
            sysLog.setMethod(className   "."   methodName   "()");
            // 请求的方法参数值
            Object[] args = joinPoint.getArgs();
            // 请求的方法参数名称
            LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
            String[] paramNames = u.getParameterNames(method);
            if (args != null && paramNames != null) {
                String params = "";
                for (int i = 0; i < args.length; i  ) {
                    params  = "  "   paramNames[i]   ": "   args[i];
                }
                sysLog.setParams(params);
            }
            // 获取request
            HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
            // 设置IP地址
            sysLog.setIp(IPUtils.getIpAddr(request));
            //获取用户名
            Subject subject = SecurityUtils.getSubject();
            SysUser user = (SysUser) subject.getPrincipal();
            if (user != null) {
                sysLog.setUserName(user.getUname());
            }
            //获取执行时长
            sysLog.setTime((int) time);
            //获取请求时间
            sysLog.setCreateTime(new Date());
            //获取系统和浏览器
            UserAgent userAgent = UserAgent.parseUserAgentString(request.getHeader("User-Agent"));
            if (userAgent != null) {
                Browser browser = userAgent.getBrowser();
                if (browser != null) {
                    Version browserVersion = userAgent.getBrowserVersion();
                    if (browserVersion != null) {
                        sysLog.setBrowser(browser.getName()   "/"   browserVersion.getVersion());
                    } else {
                        sysLog.setBrowser(browser.getName());
                    }
    
                }
                OperatingSystem operatingSystem = userAgent.getOperatingSystem();
                if (operatingSystem != null) {
                    sysLog.setSystem(operatingSystem.getName());
                }
            }
            // 保存系统日志
            sysLogService.insert(sysLog);
        }
    }

7. 配置工具类

代码语言:javascript复制
    /**
     * 获取用户真实的ip地址
     * @author 。。。源
     * @date 2020/6/22 3:12 下午
     * @email apple_dzy@163.com
     */
    public class IPUtils {
        /**
         * 获取IP地址
         * <p>
         * 使用Nginx等反向代理软件, 则不能通过request.getRemoteAddr()获取IP地址
         * 如果使用了多级反向代理的话,X-Forwarded-For的值并不止一个,而是一串IP地址,X-Forwarded-For中第一个非unknown的有效IP字符串,则为真实IP地址
         */
        public static String getIpAddr(HttpServletRequest request) {
            String ip = null;
    
            //X-Forwarded-For:Squid 服务代理
            String ipAddresses = request.getHeader("X-Forwarded-For");
            String unknown = "unknown";
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //Proxy-Client-IP:apache 服务代理
                ipAddresses = request.getHeader("Proxy-Client-IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //WL-Proxy-Client-IP:weblogic 服务代理
                ipAddresses = request.getHeader("WL-Proxy-Client-IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //HTTP_CLIENT_IP:有些代理服务器
                ipAddresses = request.getHeader("HTTP_CLIENT_IP");
            }
    
            if (ipAddresses == null || ipAddresses.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                //X-Real-IP:nginx服务代理
                ipAddresses = request.getHeader("X-Real-IP");
            }
    
            //有些网络通过多层代理,那么获取到的ip就会有多个,一般都是通过逗号(,)分割开来,并且第一个ip为客户端的真实IP
            if (ipAddresses != null && ipAddresses.length() != 0) {
                ip = ipAddresses.split(",")[0];
            }
    
            //还是不能获取到,最后再通过request.getRemoteAddr();获取
            if (ip == null || ip.length() == 0 || unknown.equalsIgnoreCase(ipAddresses)) {
                ip = request.getRemoteAddr();
            }
            return ip;
        }
    }
代码语言:javascript复制
    public class HttpContextUtils {
        public static HttpServletRequest getHttpServletRequest() {
            return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        }
    }

8. 测试

代码语言:javascript复制
    /**
     * @author 。。。源
     * @date 2020/7/24
     * @email apple_dzy@163.com
     */
    @RestController
    @RequestMapping("/sys_log")
    public class SysLogController {
    
        @Autowired
        private SysLogService logService;
    
        @Log("查询系统日志")
        @PostMapping("/query")
        public Json query(@RequestBody String body) {
            JSONObject json = JSON.parseObject(body);
            String userName = json.getString("userName");
            IPage<SysLog> page = logService.queryLog(PageUtils.getPageParam(json), userName);
            return Json.succ(oper).data("page", page);
        }
    
    
    }

版权属于:。。。源

本文链接:https://cloud.tencent.com/developer/article/2169618

转载时须注明出处及本声明。我的博客即将同步至腾讯云开发者社区,邀请大家一同入驻:https://cloud.tencent.com/developer/support-plan?invite_code=2py75w7904qok

0 人点赞