惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
F
Full Disclosure
月光博客
月光博客
MongoDB | Blog
MongoDB | Blog
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
T
Tailwind CSS Blog
Microsoft Security Blog
Microsoft Security Blog
N
Netflix TechBlog - Medium
The Register - Security
The Register - Security
aimingoo的专栏
aimingoo的专栏
The GitHub Blog
The GitHub Blog
D
Docker
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
I
InfoQ
腾讯CDC
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
K
Kaspersky official blog
Hugging Face - Blog
Hugging Face - Blog
D
DataBreaches.Net
P
Proofpoint News Feed
Cisco Talos Blog
Cisco Talos Blog
NISL@THU
NISL@THU
有赞技术团队
有赞技术团队
P
Proofpoint News Feed
美团技术团队
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Martin Fowler
Martin Fowler
L
Lohrmann on Cybersecurity
Know Your Adversary
Know Your Adversary
Simon Willison's Weblog
Simon Willison's Weblog
S
Securelist
T
Threatpost
云风的 BLOG
云风的 BLOG
G
Google Developers Blog
L
LangChain Blog
Blog — PlanetScale
Blog — PlanetScale
Recorded Future
Recorded Future
C
Cisco Blogs
S
SegmentFault 最新的问题
Cyberwarzone
Cyberwarzone
GbyAI
GbyAI
IT之家
IT之家
S
Schneier on Security
L
LINUX DO - 热门话题
T
Tenable Blog
T
The Exploit Database - CXSecurity.com
MyScale Blog
MyScale Blog
P
Privacy & Cybersecurity Law Blog
Google Online Security Blog
Google Online Security Blog
T
Threat Research - Cisco Blogs

博客园 - 华安

Unity 相机:正交 (Orthographic) vs 透视 (Perspective) - 华安 unity中的button中的onclick控制面板中四个参数的意思分别是什么 - 华安 微信小程序开发中触发 onshow的几种特殊情况 SQL Server备份心得 MYSQL 备份数据库 微信与支付支付功能开发 微信小程序中页面配置下拉刷新 unity中 相机没有视锥效果线框了,如何打开 JSONPath表达式 C# 中的操作JSON类 JObject cookie中的 HttpOnly 、Secure、SameSite 解释 Spring-boot 中基于 IP 的限流和自动封禁 Filter 登录 用 HMAC-SHA256 实现 TwoFA(二重验证)的坑 pixi-filters中的BackdropBlurFilter使用注意事项 PixiJS中的 SplitBitmapText.chars中的每个 BitmapText的x值分析 legend隐藏不想显示的图例 spring-boot HttpServletResponse response.sendRedirect是会跳转到 http而不是https springboot获取post请求参数 Javascript如何判断是触摸屏还是PC端 JavaScript获取鼠标点一个元素,获取鼠标点击的元素内的位置 spring-boot中配置Mongodbd的问题小结 Rest Template中添加 PATCH请求。 CSS实现修改CheckBox样式 SpringBoot+Thyemleaf报错:Error resolving template Template might not exist or might not be accessible div display flex 如何出现横向滚动条
利用Spring Boot的 filter 结合ConcurrentHashMap 实现“同一IP每分钟最多允许300个404请求,超出后禁用30分钟访问”
华安 · 2026-01-23 · via 博客园 - 华安

实现思路:

  • 使用两个ConcurrentHashMap
    • 一个存储每个IP的请求统计信息(请求数、404数、统计更新时间)
    • 一个存储被封禁的IP及封禁到期时间
  • 通过过滤器中的doFilter(),在请求前检查是否被封禁,在请求后检查响应状态码,统计404次数
  • 超过404阈值时封禁IP,直到30分钟后自动解封

代码示例

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.concurrent.ConcurrentHashMap;

@WebFilter(urlPatterns = "/*")
public class RateLimitFilter implements Filter {

    private static class IpStats {
        int requestCount = 0;
        int error404Count = 0;
        long lastResetTime = System.currentTimeMillis();
    }

    // 存储IP的请求统计信息
    private final ConcurrentHashMap<String, IpStats> ipStatsMap = new ConcurrentHashMap<>();
    // 存储被封禁的IP及其解封时间
    private final ConcurrentHashMap<String, Long> blackListMap = new ConcurrentHashMap<>();

    // 配置参数
    private static final int MAX_REQUESTS_PER_MINUTE = 300;
    private static final int MAX_404_COUNT = 300;
    private static final long BLOCK_TIME_MILLIS = 30 * 60 * 1000; // 30分钟

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        String ip = request.getRemoteAddr();
        long currentTime = System.currentTimeMillis();

        // 检查是否被封禁
        Long blockUntil = blackListMap.get(ip);
        if (blockUntil != null && blockUntil > currentTime) {
            ((HttpServletResponse) response).setStatus(429); // 太多请求
            response.getWriter().write("访问受限,请稍后重试。");
            return;
        } else if (blockUntil != null && blockUntil <= currentTime) {
            // 解封
            blackListMap.remove(ip);
        }

        // 统计请求信息
        IpStats stats = ipStatsMap.computeIfAbsent(ip, k -> new IpStats());

        // 存在跨分钟的情况,重置统计
        if (currentTime - stats.lastResetTime > 60 * 1000) {
            stats.requestCount = 0;
            stats.error404Count = 0;
            stats.lastResetTime = currentTime;
        }

        // 请求计数
        stats.requestCount++;
        ipStatsMap.put(ip, stats);

        // 处理请求
        // 使用自定义Response包装器捕获状态码
        StatusCaptureResponseWrapper wrappedResponse = new StatusCaptureResponseWrapper((HttpServletResponse) response);
        chain.doFilter(request, wrappedResponse);

        // 判断响应状态码
        if (wrappedResponse.getStatus() == 404) {
            stats.error404Count++;
        }

        // 超过404阈值,加入黑名单
        if (stats.error404Count >= MAX_404_COUNT) {
            blackListMap.put(ip, currentTime + BLOCK_TIME_MILLIS);
            // 可选: 清理统计数据
            ipStatsMap.remove(ip);
        }
    }

    /**
     * 自定义ResponseWrapper,用于捕获状态码
     */
    private static class StatusCaptureResponseWrapper extends HttpServletResponseWrapper {
        private int httpStatus = 200;

        public StatusCaptureResponseWrapper(HttpServletResponse response) {
            super(response);
        }

        @Override
        public void setStatus(int sc) {
            super.setStatus(sc);
            this.httpStatus = sc;
        }

        @Override
        public void sendError(int sc) throws IOException {
            super.sendError(sc);
            this.httpStatus = sc;
        }

        @Override
        public void sendError(int sc, String msg) throws IOException {
            super.sendError(sc, msg);
            this.httpStatus = sc;
        }public int getStatus() {
            return this.httpStatus;
        }
    }
}

配置说明

  • 这个Filter需要添加到Spring Boot中,可以通过@WebFilter注解,或者在配置类中注册。
  • 请求到达时,先检查IP是否被封禁。
  • 请求结束后,根据响应状态码更新404计数。
  • 超过404阈值,会封禁IP30分钟。

小结

  • 这是一个纯Filter方案示例,适合单实例部署。
  • 生产环境建议结合缓存(如Redis)或中间件以支持多实例。