-
- <dependency>
- <groupId>org.springframework.bootgroupId>
- <artifactId>spring-boot-starter-aopartifactId>
- dependency>
- package com.example.springboot.entity;
-
- import com.baomidou.mybatisplus.annotation.IdType;
- import com.baomidou.mybatisplus.annotation.TableId;
- import com.baomidou.mybatisplus.annotation.TableName;
- import java.io.Serializable;
- import java.time.LocalDateTime;
- import lombok.Getter;
- import lombok.Setter;
-
- /**
- *
- * 请求日志
- *
- *
- * @author Sca_jie
- * @since 2023-09-28
- */
- @Getter
- @Setter
- @TableName("request_log")
- public class RequestLog implements Serializable {
-
- private static final long serialVersionUID = 1L;
-
- // 主键-自增
- @TableId(value = "number", type = IdType.AUTO)
- private Integer number;
-
- // 用户账号
- private String id;
-
- // 携带token
- private String token;
-
- // 接口路径
- private String url;
-
- // 请求类型
- private String method;
-
- // 携带参数
- private String params;
-
- // ip地址
- private String ip;
-
- // 结果
- private String result;
-
- // 接口发起时间
- private LocalDateTime startDate;
-
- // 接口结束时间
- private LocalDateTime endDate;
-
- // 响应耗时
- private String responseTime;
- }
- package com.example.springboot.annotation;
-
- import java.lang.annotation.*;
-
- /**
- * 请求记录日志注解
- */
- @Target({ElementType.TYPE, ElementType.METHOD}) //注解放置的目标位置,METHOD是可注解在方法级别上
- @Retention(RetentionPolicy.RUNTIME) //注解在哪个阶段执行
- @Documented
- public @interface RequestLogAnnotation {
- String value() default "";
- }
- package com.example.springboot.common;
-
- import cn.hutool.core.net.NetUtil;
- import cn.hutool.json.JSONUtil;
- import com.alibaba.fastjson.JSONObject;
- import com.example.springboot.annotation.RequestLogAnnotation;
- import com.example.springboot.entity.RequestLog;
- import com.example.springboot.mapper.RequestLogMapper;
- import com.example.springboot.utils.CookieUtil;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.annotation.AfterReturning;
- import org.aspectj.lang.annotation.Aspect;
- import org.aspectj.lang.annotation.Before;
- 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 org.springframework.web.context.request.RequestContextHolder;
- import org.springframework.web.context.request.ServletRequestAttributes;
-
- import javax.servlet.http.Cookie;
- import javax.servlet.http.HttpServletRequest;
- import javax.servlet.http.HttpServletResponse;
- import java.lang.reflect.Method;
- import java.time.LocalDateTime;
-
- /**
- * 日志记录
- *
- */
- @Aspect
- @Component
- public class RequestLogAspect {
-
- @Autowired(required = false)
- RequestLogMapper requestLogMapper;
-
- /**
- * execution是给指定区域,切入点
- * annotation是让特定类使用注解,切入点
- */
- @Pointcut("execution(* com.example.springboot.controller.*.*(..)) || " +
- "@annotation(com.example.springboot.annotation.RequestLogAnnotation)")
- public void logPointCut() {
-
- }
-
- // 请求的开始处理时间(不同类型)
- Long startTime = null;
- LocalDateTime startDate;
-
- @Before("logPointCut()")
- public void beforeRequest() {
- startTime = System.currentTimeMillis();
- startDate = LocalDateTime.now();
- }
-
- @AfterReturning(value = "logPointCut()", returning = "result")
- public void saveLog(JoinPoint joinPoint, Object result) {
-
- // 获取请求头
- ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
- HttpServletRequest request = requestAttributes.getRequest();
- HttpServletResponse response = requestAttributes.getResponse();
-
- //从切面织入点处通过反射机制获取织入点处的方法
- MethodSignature signature = (MethodSignature) joinPoint.getSignature();
-
- //获取切入点所在的方法
- Method method = signature.getMethod();
-
- // 初始化日志表的实体类
- RequestLog requestLog = new RequestLog();
-
- //获取操作
- RequestLogAnnotation requestLogAnnotation = method.getAnnotation(RequestLogAnnotation.class);
-
- // // 获取@SystemLogAnnotation(value = "用户登录")中的注解value
- // if (systemLogAnnotation != null) {
- // String value = systemLogAnnotation.value();
- // requestLog.setSName(value);
- // }
-
- // 获取cookies
- Cookie[] cookies = request.getCookies();
- if (cookies != null) {
- // 获取token
- for(Cookie cookie : cookies){
- if(cookie.getName().equals("token")){
- requestLog.setToken(cookie.getValue());
- }
- }
-
- // 获取id
- String id = CookieUtil.getid(cookies);
- if (id != "" | id != null) {
- requestLog.setId(id);
- }
- }
-
- // 区分get和post获取参数
- String params = "{}";
- if (request.getMethod().equals("GET")) {
- params = JSONObject.toJSONString(request.getParameterMap());
- } else if (request.getMethod().equals("POST")) {
- params = JSONUtil.toJsonStr(joinPoint.getArgs());
- }
-
- // 获取用户真实ip地址
- String ip;
- if (request.getHeader("x-forwarded-for") == null) {
- ip = request.getRemoteAddr();
- } else {
- ip = request.getHeader("x-forwarded-for");
- }
-
- if (ip.equals("0:0:0:0:0:0:0:1")) {
- ip = "127.0.0.1";
- }
-
- // 用户Ip
- requestLog.setIp(ip);
- // 接口请求类型
- requestLog.setMethod(request.getMethod());
- // 请求参数(区分get和post)
- requestLog.setParams(params);
- // 请求接口路径
- requestLog.setUrl(request.getRequestURI().toString());
- // 返回结果
- requestLog.setResult(JSONObject.toJSONString(result));
- // 请求开始时间
- requestLog.setStartDate(startDate);
- // 请求结束时间
- requestLog.setEndDate(LocalDateTime.now());
- // 请求共计时间(ms)
- requestLog.setResponseTime(String.valueOf(System.currentTimeMillis() - startTime));
-
- // 保存日志到mysql
- requestLogMapper.insert(requestLog);
- }
- }
- @RequestLogAnnotation(value = "获取上传记录")
- @GetMapping("/getlist")
- public Result getlist (@RequestParam(required = false) String id) {
- if (id == null) {
- return Result.success(404, "参数缺失");
- } else {
- List
page = uploadLogService.getlist(id); - return Result.success(200, page.toString());
- }
-
- }
当我们项目中同时使用Interceptor拦截器和aop日志拦截时,被Interceptor拦截器所拦截的请求不会通过aop日志保存到数据库(防止恶意爬虫)。
但是项目如果需要记录这些被拦截的非法请求的话,目前暂时的解决方法是在Interceptor拦截器所拦截非法的请求之前再使用前面的RequestLogMapper再重新进行保存一次(只针对非法请求,因为合法请求会通过Aop日志拦截)。
但这时候又出现了新的问题,在Interceptor创建时mapper和service还没来得及注入,会导致mapper和service引用为null,就需要在创建前先行赋值,如下:
- @Configuration
- public class InterceptorConfig implements WebMvcConfigurer {
-
- /**
- * 白名单
- */
- private static String[] WhiteList = {"/user/login", "/user/register"};
-
- /**
- * 解决在Token拦截器中无法使用mapper和service的情况(无Bean)
- * @return
- */
- @Bean
- public TokenInterceptor myTokenInterceptor () {
- return new TokenInterceptor();
- }
-
- /**
- * http请求拦截器
- * @param registry
- */
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
-
- // 除excludePathPatterns内包含的接口,其他接口都要经过拦截,执行LogInterceptor()
- registry.addInterceptor(myTokenInterceptor())
- .excludePathPatterns(WhiteList);
- }
- }