MyBatis + Spring Boot 的完整 IP 日志记录链路

基于 MyBatis + Spring Boot 的完整 IP 日志记录链路,包括:

✅ 项目结构

└── com.example.iplog
    ├── config/           ← Web 拦截器与线程池配置
    ├── controller/       ← 可选:用于测试
    ├── interceptor/      ← IP 日志拦截器
    ├── mapper/           ← MyBatis Mapper 接口
    ├── model/            ← 实体类
    ├── service/impl/     ← Service 实现
    └── util/             ← IP 提取工具

1️⃣ 数据库建表 SQL

CREATE TABLE sys_ip_log (
  id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
  username VARCHAR(64) NOT NULL COMMENT '用户名',
  ip VARCHAR(45) NOT NULL COMMENT '客户端IP',
  request_url VARCHAR(255) NOT NULL COMMENT '请求地址',
  method VARCHAR(10) DEFAULT NULL COMMENT '请求方式',
  user_agent VARCHAR(255) DEFAULT NULL COMMENT '用户代理',
  trace_id VARCHAR(64) DEFAULT NULL COMMENT '链路追踪ID',
  status_code INT DEFAULT NULL COMMENT '响应状态码',
  create_time DATETIME DEFAULT CURRENT_TIMESTAMP COMMENT '记录时间'
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='IP访问日志';

2️⃣ 实体类 SysIpLog

@Data
public class SysIpLog {
    private Long id;
    private String username;
    private String ip;
    private String requestUrl;
    private String method;
    private String userAgent;
    private String traceId;
    private Integer statusCode;
    private LocalDateTime createTime;
}

3️⃣ Mapper 接口与 XML

接口:SysIpLogMapper.java

@Mapper
public interface SysIpLogMapper {
    void insert(SysIpLog log);
}

XML 映射:SysIpLogMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
                       "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.example.iplog.mapper.SysIpLogMapper">

  <insert id="insert" parameterType="com.example.iplog.model.SysIpLog">
    INSERT INTO sys_ip_log (
      username, ip, request_url, method,
      user_agent, trace_id, status_code, create_time
    ) VALUES (
      #{username}, #{ip}, #{requestUrl}, #{method},
      #{userAgent}, #{traceId}, #{statusCode}, now()
    )
  </insert>

</mapper>

4️⃣ IP 提取工具类 IpUtil

public class IpUtil {
    public static String getClientIp(HttpServletRequest request) {
        String[] headers = {
            "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP",
            "HTTP_CLIENT_IP", "HTTP_X_FORWARDED_FOR"
        };
        for (String header : headers) {
            String ip = request.getHeader(header);
            if (StringUtils.hasText(ip) && !"unknown".equalsIgnoreCase(ip)) {
                return ip.split(",")[0].trim();
            }
        }
        return request.getRemoteAddr();
    }
}

5️⃣ Service 接口 & 实现

接口:IpLogService

public interface IpLogService {
    void saveAsync(SysIpLog log);
}

实现:IpLogServiceImpl

@Service
public class IpLogServiceImpl implements IpLogService {

    @Autowired
    private SysIpLogMapper sysIpLogMapper;

    @Async("logExecutor")
    @Override
    public void saveAsync(SysIpLog log) {
        sysIpLogMapper.insert(log);
    }
}

6️⃣ 拦截器:IpLogInterceptor

@Component
public class IpLogInterceptor implements HandlerInterceptor {

    @Autowired
    private IpLogService ipLogService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
        String ip = IpUtil.getClientIp(request);
        String url = request.getRequestURI();
        String method = request.getMethod();
        String userAgent = request.getHeader("User-Agent");
        String traceId = request.getHeader("X-Trace-Id");
        String username = request.getUserPrincipal() != null
                ? request.getUserPrincipal().getName() : "anonymous";

        SysIpLog log = new SysIpLog();
        log.setIp(ip);
        log.setRequestUrl(url);
        log.setMethod(method);
        log.setUserAgent(userAgent);
        log.setTraceId(traceId);
        log.setUsername(username);

        ipLogService.saveAsync(log);
        return true;
    }
}

7️⃣ 拦截器注册配置:WebMvcConfig

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    @Autowired
    private IpLogInterceptor ipLogInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(ipLogInterceptor)
                .addPathPatterns("/**")
                .excludePathPatterns("/static/**", "/actuator/**");
    }
}

8️⃣ 异步线程池配置:AsyncConfig

@Configuration
@EnableAsync
public class AsyncConfig {

    @Bean("logExecutor")
    public Executor logExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(2);
        executor.setMaxPoolSize(10);
        executor.setQueueCapacity(1000);
        executor.setThreadNamePrefix("IpLogAsync-");
        executor.initialize();
        return executor;
    }
}

✅ 测试验证

你可以用一个简单的接口 /hello 访问后,看数据库是否写入成功。

🔁 拓展建议
• 支持 statusCode 记录,可用 Filter 包装响应;
• 支持多租户、TraceId 传递;
• 接入 MQ 或写入 Elasticsearch 进一步增强可观测性。

©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容

  • """1.个性化消息: 将用户的姓名存到一个变量中,并向该用户显示一条消息。显示的消息应非常简单,如“Hello ...
    她即我命阅读 3,271评论 0 5
  • 为了让我有一个更快速、更精彩、更辉煌的成长,我将开始这段刻骨铭心的自我蜕变之旅!从今天开始,我将每天坚持阅...
    李薇帆阅读 1,894评论 0 3
  • 似乎最近一直都在路上,每次出来走的时候感受都会很不一样。 1、感恩一直遇到好心人,很幸运。在路上总是...
    时间里的花Lily阅读 1,338评论 0 1
  • 1、expected an indented block 冒号后面是要写上一定的内容的(新手容易遗忘这一点); 缩...
    庵下桃花仙阅读 508评论 0 1
  • 一、工具箱(多种工具共用一个快捷键的可同时按【Shift】加此快捷键选取)矩形、椭圆选框工具 【M】移动工具 【V...
    墨雅丫阅读 524评论 0 0