SpringBoot 自研「轻量级 API 防火墙」:单机内嵌,支持在线配置

1. 背景与痛点

在做后端开发时,我们常常会遇到这样的困境:

接口被恶意刷流量:比如某个查询接口被短时间大量调用,数据库连接数打满,最终拖垮整个服务。

缺少细粒度防护能力:很多系统只有粗糙的全局限流,但某些高价值 API(比如下单、支付、导出)并没有单独保护,一旦被攻击,影响范围很大。

风控策略难以落地:规则写死在代码里,每次调整都要改代码、打包、上线,运维成本高。

黑白名单管理混乱:一些临时封禁规则放在 Nginx,一些写在数据库,开发、运维、测试之间没有统一入口。

很多企业会引入 API 网关(如 Kong、Spring Cloud Gateway、Nginx+Lua)来解决,但这类方案往往过于"重量级":

需要额外维护一层组件,增加了部署复杂度。 学习和使用成本高,不适合中小团队快速落地。 对于单机部署或内网小系统来说,显得"杀鸡用牛刀"。

因此,一个非常现实的需求出现了:
在 Spring Boot 应用中内嵌一个轻量级 API 防火墙,做到单机级别的限流 + 风控 + 在线配置,无需依赖网关,也能快速解决安全与稳定性问题。


2. 设计思路

目标:在 SpringBoot 内嵌一层防护层,对所有 API 请求进行"前置检查"。

核心能力拆解:

黑白名单:支持配置IP白名单、黑名单;白名单优先级更高。

限流策略

diff 复制代码
-   QPS 限流(如单接口每秒不超过 100 次请求)。
-   时间窗限流(如同一用户 1 分钟不超过 60 次调用)。
-   突发流量控制(避免短时间内瞬时压垮系统)。

风控规则:支持基于时间窗口的访问频率控制,如"单个IP在60秒内最多访问10次"。

在线配置:提供前端控制台,实时修改规则,立即生效,无需重启。

低侵入性 :以 InterceptorFilter 形式接入,不影响现有业务逻辑。

架构示意:

markdown 复制代码
 ┌────────────────────┐
 │   API Firewall     │ ← 拦截层(黑白名单 / 限流 / 风控)
 └────────────────────┘
             ↓
 ┌────────────────────┐
 │   应用业务 API      │
 └────────────────────┘

3. 技术选型

Spring Boot Interceptor:最适合做请求前置校验,侵入性低。

Guava Cache:实现高性能内存缓存,支持过期策略和并发访问,也可使用Caffeine。

AtomicInteger + 时间窗口:基于计数器的限流实现,简单高效。

Spring Boot Actuator:提供健康检查和监控端点。

前端页面:Tailwind CSS + Chart.js:现代化响应式管理界面。

对于分布式场景,后续可以扩展 Redis + Lua 实现统一限流;但在本文场景下,先聚焦 单机轻量化防护


4. 核心实现

4.1 定义规则实体

java 复制代码
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class FirewallRule {
    
    /**
     * 主键ID
     */
    private Long id;
    
    /**
     * 规则名称
     */
    private String ruleName;
    
    /**
     * API路径匹配模式
     */
    private String apiPattern;
    
    /**
     * QPS限制(每秒最大请求数)
     */
    private Integer qpsLimit;
    
    /**
     * 单用户时间窗限制(分钟内最大请求数)
     */
    private Integer userLimit;
    
    /**
     * 时间窗口(秒)
     */
    private Integer timeWindow;
    
    /**
     * 是否启用
     */
    private Boolean enabled;
    
    /**
     * 规则描述
     */
    private String description;
    
    /**
     * 创建时间
     */
    private LocalDateTime createdTime;
    
    /**
     * 更新时间
     */
    private LocalDateTime updatedTime;
    
    /**
     * 检查API路径是否匹配此规则
     * 
     * @param apiPath API路径
     * @return 是否匹配
     */
    public boolean matches(String apiPath) {
        if (apiPattern == null || apiPath == null) {
            return false;
        }
        
        // 支持通配符匹配
        String pattern = apiPattern.replace("**", ".*").replace("*", "[^/]*");
        return apiPath.matches(pattern);
    }
}

4.2 规则管理器

java 复制代码
@Slf4j
@Service
public class RuleManager {
    
    @Autowired
    private FirewallRuleMapper ruleMapper;
    
    @Autowired
    private FirewallBlacklistMapper blacklistMapper;
    
    @Autowired
    private FirewallWhitelistMapper whitelistMapper;
    
    /**
     * 规则缓存
     */
    private final Map<String, FirewallRule> ruleCache = new ConcurrentHashMap<>();
    
    /**
     * 黑名单缓存
     */
    private final Map<String, FirewallBlacklist> blacklistCache = new ConcurrentHashMap<>();
    
    /**
     * 白名单缓存
     */
    private final Map<String, FirewallWhitelist> whitelistCache = new ConcurrentHashMap<>();
    
    /**
     * 初始化加载规则
     */
    @PostConstruct
    public void init() {
        log.info("初始化防火墙规则管理器...");
        refreshRules();
        refreshBlacklist();
        refreshWhitelist();
        log.info("防火墙规则管理器初始化完成,加载规则: {}, 黑名单: {}, 白名单: {}", 
                ruleCache.size(), blacklistCache.size(), whitelistCache.size());
    }
    
    /**
     * 获取匹配指定API路径的规则
     * 
     * @param apiPath API路径
     * @return 匹配的规则,如果没有匹配则返回null
     */
    public FirewallRule getMatchingRule(String apiPath) {
        if (apiPath == null) {
            return null;
        }
        
        // 优先精确匹配
        FirewallRule exactMatch = ruleCache.get(apiPath);
        if (exactMatch != null && exactMatch.isEffectiveEnabled()) {
            return exactMatch;
        }
        
        // 模式匹配
        for (FirewallRule rule : ruleCache.values()) {
            if (rule.isEffectiveEnabled() && rule.matches(apiPath)) {
                return rule;
            }
        }
        
        return null;
    }
    
    /**
     * 检查IP是否在黑名单中
     * 
     * @param ipAddress IP地址
     * @return 是否在黑名单中
     */
    public boolean isBlacklisted(String ipAddress) {
        if (ipAddress == null) {
            return false;
        }
        
        for (FirewallBlacklist blacklist : blacklistCache.values()) {
            if (blacklist.isValid() && blacklist.matches(ipAddress)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 检查IP是否在白名单中
     * 
     * @param ipAddress IP地址
     * @return 是否在白名单中
     */
    public boolean isWhitelisted(String ipAddress) {
        if (ipAddress == null) {
            return false;
        }
        
        for (FirewallWhitelist whitelist : whitelistCache.values()) {
            if (whitelist.isValid() && whitelist.matches(ipAddress)) {
                return true;
            }
        }
        
        return false;
    }
    
    /**
     * 定时刷新缓存
     */
    @Scheduled(fixedRate = 300000) // 每5分钟刷新一次
    public void scheduledRefresh() {
        try {
            refreshRules();
            refreshBlacklist();
            refreshWhitelist();
            log.debug("定时刷新防火墙规则缓存完成");
        } catch (Exception e) {
            log.error("定时刷新防火墙规则缓存失败", e);
        }
    }
}

4.3 拦截器实现逻辑

java 复制代码
@Slf4j
@Component
public class FirewallInterceptor implements HandlerInterceptor {

    @Autowired
    private RuleManager ruleManager;
    
    @Autowired
    private FirewallService firewallService;
    
    @Value("${firewall.default.qps-limit:100}")
    private int defaultQpsLimit;
    
    @Value("${firewall.default.user-limit:1000}")
    private int defaultUserLimit;
    
    @Value("${firewall.default.time-window:60}")
    private int defaultTimeWindow;
    
    /**
     * QPS限制缓存 - 存储每个IP+API的访问计数
     */
    private final Cache<String, AtomicInteger> qpsCache = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(1, TimeUnit.MINUTES)
            .build();
    
    /**
     * 用户限制缓存 - 存储每个IP的访问计数
     */
    private final Cache<String, AtomicInteger> userCache = CacheBuilder.newBuilder()
            .maximumSize(10000)
            .expireAfterWrite(1, TimeUnit.HOURS)
            .build();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        long startTime = System.currentTimeMillis();
        String ipAddress = getClientIpAddress(request);
        String apiPath = request.getRequestURI();
        String userAgent = request.getHeader("User-Agent");
        String method = request.getMethod();
        
        log.debug("防火墙拦截检查: IP={}, API={}, Method={}", ipAddress, apiPath, method);
        
        try {
            // 1. 检查白名单
            if (ruleManager.isWhitelisted(ipAddress)) {
                log.debug("IP {} 在白名单中,允许访问", ipAddress);
                logAccess(ipAddress, apiPath, userAgent, method, 200, null, startTime);
                return true;
            }
            
            // 2. 检查黑名单
            if (ruleManager.isBlacklisted(ipAddress)) {
                log.warn("IP {} 在黑名单中,拒绝访问", ipAddress);
                blockRequest(response, "IP地址被列入黑名单", 403);
                logAccess(ipAddress, apiPath, userAgent, method, 403, "IP黑名单拦截", startTime);
                return false;
            }
            
            // 3. 获取匹配的防火墙规则
            FirewallRule rule = ruleManager.getMatchingRule(apiPath);
            if (rule == null) {
                // 使用默认规则
                rule = createDefaultRule(apiPath);
            }
            
            // 4. QPS限制检查
            if (!checkQpsLimit(ipAddress, apiPath, rule)) {
                log.warn("IP {} 访问 {} 超过QPS限制 {}", ipAddress, apiPath, rule.getEffectiveQpsLimit());
                blockRequest(response, "访问频率过高,请稍后再试", 429);
                logAccess(ipAddress, apiPath, userAgent, method, 429, "QPS限制拦截", startTime);
                return false;
            }
            
            // 5. 用户限制检查
            if (!checkUserLimit(ipAddress, rule)) {
                log.warn("IP {} 超过用户限制 {}", ipAddress, rule.getEffectiveUserLimit());
                blockRequest(response, "访问次数超过限制,请稍后再试", 429);
                logAccess(ipAddress, apiPath, userAgent, method, 429, "用户限制拦截", startTime);
                return false;
            }
            
            // 6. 记录正常访问
            logAccess(ipAddress, apiPath, userAgent, method, 200, null, startTime);
            log.debug("IP {} 访问 {} 通过防火墙检查", ipAddress, apiPath);
            return true;
            
        } catch (Exception e) {
            log.error("防火墙拦截器处理异常: IP={}, API={}", ipAddress, apiPath, e);
            logAccess(ipAddress, apiPath, userAgent, method, 500, "系统异常", startTime);
            return true; // 异常情况下允许通过,避免影响正常业务
        }
    }
    
    /**
     * 检查QPS限制
     */
    private boolean checkQpsLimit(String ipAddress, String apiPath, FirewallRule rule) {
        String key = ipAddress + ":" + apiPath;
        int qpsLimit = rule.getEffectiveQpsLimit();
        
        if (qpsLimit <= 0) {
            return true; // 无限制
        }
        
        AtomicInteger counter = qpsCache.getIfPresent(key);
        if (counter == null) {
            counter = new AtomicInteger(0);
            qpsCache.put(key, counter);
        }
        
        int currentCount = counter.incrementAndGet();
        return currentCount <= qpsLimit;
    }
    
    /**
     * 检查用户限制
     */
    private boolean checkUserLimit(String ipAddress, FirewallRule rule) {
        int userLimit = rule.getEffectiveUserLimit();
        
        if (userLimit <= 0) {
            return true; // 无限制
        }
        
        AtomicInteger counter = userCache.getIfPresent(ipAddress);
        if (counter == null) {
            counter = new AtomicInteger(0);
            userCache.put(ipAddress, counter);
        }
        
        int currentCount = counter.incrementAndGet();
        return currentCount <= userLimit;
    }
    
    /**
     * 获取客户端真实IP地址
     */
    private String getClientIpAddress(HttpServletRequest request) {
        String xForwardedFor = request.getHeader("X-Forwarded-For");
        if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
            return xForwardedFor.split(",")[0].trim();
        }
        
        String xRealIp = request.getHeader("X-Real-IP");
        if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
            return xRealIp;
        }
        
        return request.getRemoteAddr();
    }
}

4.4 在线配置接口

java 复制代码
@Slf4j
@RestController
@RequestMapping("/api/firewall")
@CrossOrigin(origins = "*") // 允许跨域访问
public class FirewallController {
    
    @Autowired
    private RuleManager ruleManager;
    
    @Autowired
    private FirewallService firewallService;
    
    @Autowired
    private FirewallInterceptor firewallInterceptor;
    
    /**
     * 获取所有防火墙规则
     */
    @GetMapping("/rules")
    public ResponseEntity<List<FirewallRule>> getRules() {
        try {
            List<FirewallRule> rules = ruleManager.getAllRules();
            return ResponseEntity.ok(rules);
        } catch (Exception e) {
            log.error("获取防火墙规则失败", e);
            return ResponseEntity.status(500).build();
        }
    }
    
    /**
     * 创建或更新防火墙规则
     */
    @PostMapping("/rules")
    public ResponseEntity<Map<String, Object>> saveRule(@RequestBody FirewallRule rule) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = ruleManager.saveRule(rule);
            if (success) {
                result.put("success", true);
                result.put("message", "规则保存成功");
                return ResponseEntity.ok(result);
            } else {
                result.put("success", false);
                result.put("message", "规则保存失败");
                return ResponseEntity.status(500).body(result);
            }
        } catch (Exception e) {
            log.error("保存防火墙规则失败", e);
            result.put("success", false);
            result.put("message", "系统异常: " + e.getMessage());
            return ResponseEntity.status(500).body(result);
        }
    }
    
    /**
     * 删除防火墙规则
     */
    @DeleteMapping("/rules/{id}")
    public ResponseEntity<Map<String, Object>> deleteRule(@PathVariable Long id) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = ruleManager.deleteRule(id);
            if (success) {
                result.put("success", true);
                result.put("message", "规则删除成功");
                return ResponseEntity.ok(result);
            } else {
                result.put("success", false);
                result.put("message", "规则删除失败");
                return ResponseEntity.status(500).body(result);
            }
        } catch (Exception e) {
            log.error("删除防火墙规则失败", e);
            result.put("success", false);
            result.put("message", "系统异常: " + e.getMessage());
            return ResponseEntity.status(500).body(result);
        }
    }
    
    /**
     * 获取黑名单列表
     */
    @GetMapping("/blacklist")
    public ResponseEntity<List<FirewallBlacklist>> getBlacklist() {
        try {
            List<FirewallBlacklist> blacklist = ruleManager.getAllBlacklist();
            return ResponseEntity.ok(blacklist);
        } catch (Exception e) {
            log.error("获取黑名单失败", e);
            return ResponseEntity.status(500).build();
        }
    }
    
    /**
     * 添加黑名单
     */
    @PostMapping("/blacklist")
    public ResponseEntity<Map<String, Object>> addBlacklist(@RequestBody FirewallBlacklist blacklist) {
        Map<String, Object> result = new HashMap<>();
        try {
            boolean success = ruleManager.addBlacklist(blacklist);
            if (success) {
                result.put("success", true);
                result.put("message", "黑名单添加成功");
                return ResponseEntity.ok(result);
            } else {
                result.put("success", false);
                result.put("message", "黑名单添加失败");
                return ResponseEntity.status(500).body(result);
            }
        } catch (Exception e) {
            log.error("添加黑名单失败", e);
            result.put("success", false);
            result.put("message", "系统异常: " + e.getMessage());
            return ResponseEntity.status(500).body(result);
        }
    }
    
    /**
     * 获取访问日志
     */
    @GetMapping("/logs")
    public ResponseEntity<List<FirewallAccessLog>> getLogs(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam(defaultValue = "20") int size,
            @RequestParam(required = false) String ipAddress,
            @RequestParam(required = false) String apiPath) {
        try {
            List<FirewallAccessLog> logs = firewallService.getAccessLogs(page, size, ipAddress, apiPath);
            return ResponseEntity.ok(logs);
        } catch (Exception e) {
            log.error("获取访问日志失败", e);
            return ResponseEntity.status(500).build();
        }
    }
    
    /**
     * 获取统计数据
     */
    @GetMapping("/statistics")
    public ResponseEntity<Map<String, Object>> getStatistics(
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate startDate,
            @RequestParam @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate endDate) {
        try {
            Map<String, Object> statistics = firewallService.getStatistics(startDate, endDate);
            return ResponseEntity.ok(statistics);
        } catch (Exception e) {
            log.error("获取统计数据失败", e);
            return ResponseEntity.status(500).build();
        }
    }
}

这样,我们就有了一个"可实时更新"的防火墙层。


5. 前端控制台

一个现代化的管理页面,基于 HTML5 + Tailwind CSS + JavaScript,提供完整的防火墙管理功能:

html 复制代码
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Web防火墙控制台</title>
    <script src="https://cdn.tailwindcss.com"></script>
    <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.0.0/css/all.min.css" rel="stylesheet">
    <script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
</head>
<body class="bg-gray-50 font-sans">
    <div class="min-h-screen">
        <!-- 顶部导航栏 -->
        <nav class="bg-white shadow-lg border-b border-gray-200">
            <div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
                <div class="flex justify-between h-16">
                    <!-- 左侧 Logo 和导航 -->
                    <div class="flex items-center space-x-8">
                        <div class="flex items-center space-x-3">
                            <div class="bg-blue-600 p-2 rounded-lg">
                                <i class="fas fa-shield-alt text-white text-xl"></i>
                            </div>
                            <span class="text-xl font-bold text-gray-900">Web防火墙</span>
                        </div>
                        
                        <!-- 主导航菜单 -->
                        <div class="hidden md:flex space-x-4">
                            <button class="nav-btn active" data-page="dashboard">
                                <i class="fas fa-tachometer-alt mr-2 text-blue-500"></i>仪表盘
                            </button>
                            <button class="nav-btn" data-page="rules">
                                <i class="fas fa-list-ul mr-2 text-green-500"></i>规则管理
                            </button>
                            <button class="nav-btn" data-page="blacklist">
                                <i class="fas fa-ban mr-2 text-red-500"></i>黑名单
                            </button>
                            <button class="nav-btn" data-page="whitelist">
                                <i class="fas fa-check-circle mr-2 text-emerald-500"></i>白名单
                            </button>
                            <button class="nav-btn" data-page="logs">
                                <i class="fas fa-file-alt mr-2 text-purple-500"></i>访问日志
                            </button>
                        </div>
                    </div>
                </div>
            </div>
        </nav>

        <!-- 主内容区 -->
        <main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
            <!-- 仪表盘页面 -->
            <div id="dashboard-page" class="page-content">
                <div class="mb-8">
                    <h1 class="text-3xl font-bold text-gray-900">仪表盘</h1>
                    <p class="text-gray-600 mt-2">实时监控系统状态和安全指标</p>
                </div>
                
                <!-- 统计卡片 -->
                <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
                    <div class="bg-white rounded-lg shadow p-6">
                        <div class="flex items-center">
                            <div class="p-2 bg-blue-100 rounded-lg">
                                <i class="fas fa-globe text-blue-600 text-xl"></i>
                            </div>
                            <div class="ml-4">
                                <p class="text-sm font-medium text-gray-600">今日请求</p>
                                <p class="text-2xl font-bold text-gray-900" id="today-requests">0</p>
                            </div>
                        </div>
                    </div>
                    
                    <div class="bg-white rounded-lg shadow p-6">
                        <div class="flex items-center">
                            <div class="p-2 bg-red-100 rounded-lg">
                                <i class="fas fa-shield-alt text-red-600 text-xl"></i>
                            </div>
                            <div class="ml-4">
                                <p class="text-sm font-medium text-gray-600">今日拦截</p>
                                <p class="text-2xl font-bold text-gray-900" id="today-blocked">0</p>
                            </div>
                        </div>
                    </div>
                    
                    <div class="bg-white rounded-lg shadow p-6">
                        <div class="flex items-center">
                            <div class="p-2 bg-green-100 rounded-lg">
                                <i class="fas fa-check-circle text-green-600 text-xl"></i>
                            </div>
                            <div class="ml-4">
                                <p class="text-sm font-medium text-gray-600">活跃规则</p>
                                <p class="text-2xl font-bold text-gray-900" id="active-rules">0</p>
                            </div>
                        </div>
                    </div>
                    
                    <div class="bg-white rounded-lg shadow p-6">
                        <div class="flex items-center">
                            <div class="p-2 bg-yellow-100 rounded-lg">
                                <i class="fas fa-ban text-yellow-600 text-xl"></i>
                            </div>
                            <div class="ml-4">
                                <p class="text-sm font-medium text-gray-600">黑名单IP</p>
                                <p class="text-2xl font-bold text-gray-900" id="blacklist-count">0</p>
                            </div>
                        </div>
                    </div>
                </div>
                
                <!-- 图表区域 -->
                <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
                    <div class="bg-white rounded-lg shadow p-6">
                        <h3 class="text-lg font-semibold text-gray-900 mb-4">请求趋势</h3>
                        <canvas id="requestChart" width="400" height="200"></canvas>
                    </div>
                    
                    <div class="bg-white rounded-lg shadow p-6">
                        <h3 class="text-lg font-semibold text-gray-900 mb-4">拦截分布</h3>
                        <canvas id="blockChart" width="400" height="200"></canvas>
                    </div>
                </div>
            </div>
        </main>
    </div>
    
    <!-- JavaScript 应用逻辑 -->
    <script src="/js/app.js"></script>
</body>
</html>

核心 JavaScript 功能

javascript 复制代码
class FirewallApp {
    constructor() {
        this.apiBase = '/api/firewall';
        this.currentPage = 'dashboard';
        this.rules = [];
        this.charts = {};
        this.init();
    }
    
    async init() {
        this.bindEvents();
        await this.loadDashboard();
        this.startAutoRefresh();
    }
    
    // 加载仪表盘数据
    async loadDashboard() {
        try {
            const [rules, blacklist, statistics] = await Promise.all([
                this.fetchData('/rules'),
                this.fetchData('/blacklist'),
                this.fetchData('/statistics?startDate=' + this.getDateString(-7) + '&endDate=' + this.getDateString(0))
            ]);
            
            this.updateDashboardStats(rules, blacklist, statistics);
            this.updateCharts(statistics);
        } catch (error) {
            console.error('加载仪表盘数据失败:', error);
        }
    }
    
    // 更新统计数据
    updateDashboardStats(rules, blacklist, statistics) {
        document.getElementById('today-requests').textContent = statistics.todayRequests || 0;
        document.getElementById('today-blocked').textContent = statistics.todayBlocked || 0;
        document.getElementById('active-rules').textContent = rules.filter(r => r.enabled).length;
        document.getElementById('blacklist-count').textContent = blacklist.length;
    }
    
    // 更新图表
    updateCharts(statistics) {
        // 请求趋势图
        if (this.charts.requestChart) {
            this.charts.requestChart.destroy();
        }
        
        const ctx1 = document.getElementById('requestChart').getContext('2d');
        this.charts.requestChart = new Chart(ctx1, {
            type: 'line',
            data: {
                labels: statistics.dates || [],
                datasets: [{
                    label: '总请求',
                    data: statistics.requests || [],
                    borderColor: 'rgb(59, 130, 246)',
                    backgroundColor: 'rgba(59, 130, 246, 0.1)',
                    tension: 0.4
                }, {
                    label: '被拦截',
                    data: statistics.blocked || [],
                    borderColor: 'rgb(239, 68, 68)',
                    backgroundColor: 'rgba(239, 68, 68, 0.1)',
                    tension: 0.4
                }]
            },
            options: {
                responsive: true,
                maintainAspectRatio: false,
                scales: {
                    y: {
                        beginAtZero: true
                    }
                }
            }
        });
    }
    
    // 获取数据
    async fetchData(endpoint) {
        const response = await fetch(this.apiBase + endpoint);
        if (!response.ok) {
            throw new Error(`HTTP error! status: ${response.status}`);
        }
        return await response.json();
    }
    
    // 自动刷新
    startAutoRefresh() {
        setInterval(() => {
            if (this.currentPage === 'dashboard') {
                this.loadDashboard();
            }
        }, 30000); // 每30秒刷新一次
    }
}

// 初始化应用
new FirewallApp();

实时仪表盘 :显示请求量、拦截数、活跃规则等关键指标 可视化图表 :请求趋势和拦截分布的图表展示 规则管理 :在线添加、修改、删除防火墙规则 黑白名单 :IP地址的黑白名单管理 访问日志:详细的访问记录和拦截日志


6. 配置文件

application.yml 中设置完整的防火墙参数:

yaml 复制代码
server:
  port: 8080
  servlet:
    context-path: /

spring:
  application:
    name: springboot-firewall
  
  # Jackson配置
  jackson:
    serialization:
      write-dates-as-timestamps: false
    time-zone: GMT+8
    date-format: yyyy-MM-dd HH:mm:ss
  
  # 数据源配置 (H2内嵌数据库)
  datasource:
    url: jdbc:h2:mem:firewall;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
    driver-class-name: org.h2.Driver
    username: sa
    password: 
  
  # H2控制台配置
  h2:
    console:
      enabled: true
      path: /h2-console
      settings:
        web-allow-others: true
  
  # SQL初始化
  sql:
    init:
      mode: always
      schema-locations: classpath:schema.sql
      data-locations: classpath:data.sql

# MyBatis配置
mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.firewall.entity
  configuration:
    map-underscore-to-camel-case: true
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

# 管理端点配置
management:
  endpoints:
    web:
      exposure:
        include: health,info,metrics
  endpoint:
    health:
      show-details: always

# 日志配置
logging:
  level:
    com.example.firewall: DEBUG
    org.springframework.web: INFO
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n"

# 防火墙配置
firewall:
  enabled: true
  default-qps-limit: 100
  default-user-limit: 60
  cache-size: 1000
  exclude-paths:
    - /firewall/**
    - /h2-console/**
    - /actuator/**
    - /static/**
    - /favicon.ico

7. 实战应用场景

7.1 中小型系统防护

电商网站场景

yaml 复制代码
# 针对订单接口的特殊规则
- ruleName: "订单保护"
  apiPattern: "/api/orders/**"
  qpsLimit: 50
  userLimit: 5
  timeWindow: 60
  enabled: true
  description: "防止恶意刷单和重复下单"

内容平台场景

yaml 复制代码
# 评论和点赞接口限制
- ruleName: "内容互动限制"
  apiPattern: "/api/comments/**,/api/likes/**"
  qpsLimit: 200
  userLimit: 20
  timeWindow: 300
  enabled: true
  description: "防止垃圾评论和刷赞行为"

7.2 突发流量应对

营销活动期间

java 复制代码
// 通过API动态调整规则
POST /api/firewall/rules
{
    "ruleName": "秒杀活动保护",
    "apiPattern": "/api/seckill/**",
    "qpsLimit": 1000,
    "userLimit": 1,
    "timeWindow": 60,
    "enabled": true,
    "description": "秒杀活动期间特殊限制"
}

爬虫防护

java 复制代码
// 手动添加黑名单示例
FirewallBlacklist blacklist = new FirewallBlacklist();
blacklist.setIpAddress("192.168.1.100");
blacklist.setReason("恶意爬虫行为");
blacklist.setExpireTime(LocalDateTime.now().plusHours(24));
blacklist.setEnabled(true);

ruleManager.addBlacklist(blacklist);
log.info("IP {} 已手动加入黑名单", blacklist.getIpAddress());

7.3 部署和运维

Docker 部署

dockerfile 复制代码
FROM openjdk:11-jre-slim
VOLUME /tmp
COPY target/springboot-firewall-1.0.jar app.jar
EXPOSE 8080
ENTRYPOINT ["java","-jar","/app.jar"]

监控集成

yaml 复制代码
# Prometheus 监控指标
management:
  metrics:
    tags:
      application: firewall
    export:
      prometheus:
        enabled: true

总结

本文实现了一个基于 SpringBoot 的轻量级 API 防火墙,通过拦截器机制提供实时防护能力。

系统采用 Guava Cache 实现高性能内存缓存,支持 QPS 限制和用户访问频率控制。

提供了完整的黑白名单管理功能,配备现代化的 Web 管理界面,支持规则的在线配置和实时生效。整体架构简洁高效,适合中小型项目快速集成使用。

github.com/yuboon/java...

相关推荐
lvlv_feifei几秒前
是时候和单测👋🏻 -- 从验证性理论到大模型工程实践
后端
MacroZheng3 分钟前
横空出世!一款开源的数据同步工具,稳定又高效,好用到爆!
java·后端·mysql
码事漫谈7 分钟前
DeepSeek 3.1:技术突破与行业影响深度分析
后端
数字人直播10 分钟前
干货分享:AI 数字人直播怎么做才能适配多平台规则?
前端·后端
PineappleCoder14 分钟前
同源策略是啥?浏览器为啥拦我的跨域请求?(二)
前端·后端·node.js
爱可生开源社区16 分钟前
2025 年 8 月《GPT-5 家族 SQL 能力评测报告》发布
后端
喵手17 分钟前
Java中的垃圾回收机制(GC),你知道如何优化吗?
java·后端·java ee
年轻的麦子19 分钟前
Go 程序 OTA 子进程意外终止问题排查与解决
后端