⭐⭐⭐ Spring Boot 项目实战 ⭐⭐⭐ Spring Cloud 项目实战
《Dubbo 实现原理与源码解析 —— 精品合集》 《Netty 实现原理与源码解析 —— 精品合集》
《Spring 实现原理与源码解析 —— 精品合集》 《MyBatis 实现原理与源码解析 —— 精品合集》
《Spring MVC 实现原理与源码解析 —— 精品合集》 《数据库实体设计合集》
《Spring Boot 实现原理与源码解析 —— 精品合集》 《Java 面试题 + Java 学习指南》

摘要: 原创出处 风象南 「风象南」欢迎转载,保留摘要,谢谢!


🙂🙂🙂关注微信公众号:【芋道源码】有福利:

  1. RocketMQ / MyCAT / Sharding-JDBC 所有源码分析文章列表
  2. RocketMQ / MyCAT / Sharding-JDBC 中文注释源码 GitHub 地址
  3. 您对于源码的疑问每条留言都将得到认真回复。甚至不知道如何读源码也可以请教噢。
  4. 新的源码解析文章实时收到通知。每周更新一篇左右。
  5. 认真的源码交流微信群。

1. 背景与痛点

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

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

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

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

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

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

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

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

2. 设计思路

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

核心能力拆解:

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

限流策略:

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

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

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

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

架构示意:

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

3. 技术选型

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

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

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

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

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

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

4. 核心实现

4.1 定义规则实体

@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
publicclassFirewallRule {

/**
* 主键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 是否匹配
*/
publicbooleanmatches(String apiPath) {
if (apiPattern == null || apiPath == null) {
returnfalse;
}

// 支持通配符匹配
Stringpattern= apiPattern.replace("**", ".*").replace("*", "[^/]*");
return apiPath.matches(pattern);
}
}

4.2 规则管理器

@Slf4j
@Service
publicclassRuleManager {

@Autowired
private FirewallRuleMapper ruleMapper;

@Autowired
private FirewallBlacklistMapper blacklistMapper;

@Autowired
private FirewallWhitelistMapper whitelistMapper;

/**
* 规则缓存
*/
privatefinal Map<String, FirewallRule> ruleCache = newConcurrentHashMap<>();

/**
* 黑名单缓存
*/
privatefinal Map<String, FirewallBlacklist> blacklistCache = newConcurrentHashMap<>();

/**
* 白名单缓存
*/
privatefinal Map<String, FirewallWhitelist> whitelistCache = newConcurrentHashMap<>();

/**
* 初始化加载规则
*/
@PostConstruct
publicvoidinit() {
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) {
returnnull;
}

// 优先精确匹配
FirewallRuleexactMatch= ruleCache.get(apiPath);
if (exactMatch != null && exactMatch.isEffectiveEnabled()) {
return exactMatch;
}

// 模式匹配
for (FirewallRule rule : ruleCache.values()) {
if (rule.isEffectiveEnabled() && rule.matches(apiPath)) {
return rule;
}
}

returnnull;
}

/**
* 检查IP是否在黑名单中
*
* @param ipAddress IP地址
* @return 是否在黑名单中
*/
publicbooleanisBlacklisted(String ipAddress) {
if (ipAddress == null) {
returnfalse;
}

for (FirewallBlacklist blacklist : blacklistCache.values()) {
if (blacklist.isValid() && blacklist.matches(ipAddress)) {
returntrue;
}
}

returnfalse;
}

/**
* 检查IP是否在白名单中
*
* @param ipAddress IP地址
* @return 是否在白名单中
*/
publicbooleanisWhitelisted(String ipAddress) {
if (ipAddress == null) {
returnfalse;
}

for (FirewallWhitelist whitelist : whitelistCache.values()) {
if (whitelist.isValid() && whitelist.matches(ipAddress)) {
returntrue;
}
}

returnfalse;
}

/**
* 定时刷新缓存
*/
@Scheduled(fixedRate = 300000)// 每5分钟刷新一次
publicvoidscheduledRefresh() {
try {
refreshRules();
refreshBlacklist();
refreshWhitelist();
log.debug("定时刷新防火墙规则缓存完成");
} catch (Exception e) {
log.error("定时刷新防火墙规则缓存失败", e);
}
}
}

4.3 拦截器实现逻辑

@Slf4j
@Component
publicclassFirewallInterceptorimplementsHandlerInterceptor {

@Autowired
private RuleManager ruleManager;

@Autowired
private FirewallService firewallService;

@Value("${firewall.default.qps-limit:100}")
privateint defaultQpsLimit;

@Value("${firewall.default.user-limit:1000}")
privateint defaultUserLimit;

@Value("${firewall.default.time-window:60}")
privateint defaultTimeWindow;

/**
* QPS限制缓存 - 存储每个IP+API的访问计数
*/
privatefinal Cache<String, AtomicInteger> qpsCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(1, TimeUnit.MINUTES)
.build();

/**
* 用户限制缓存 - 存储每个IP的访问计数
*/
privatefinal Cache<String, AtomicInteger> userCache = CacheBuilder.newBuilder()
.maximumSize(10000)
.expireAfterWrite(1, TimeUnit.HOURS)
.build();

@Override
publicbooleanpreHandle(HttpServletRequest request, HttpServletResponse response, Object handler)throws Exception {
longstartTime= System.currentTimeMillis();
StringipAddress= getClientIpAddress(request);
StringapiPath= request.getRequestURI();
StringuserAgent= request.getHeader("User-Agent");
Stringmethod= 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);
returntrue;
}

// 2. 检查黑名单
if (ruleManager.isBlacklisted(ipAddress)) {
log.warn("IP {} 在黑名单中,拒绝访问", ipAddress);
blockRequest(response, "IP地址被列入黑名单", 403);
logAccess(ipAddress, apiPath, userAgent, method, 403, "IP黑名单拦截", startTime);
returnfalse;
}

// 3. 获取匹配的防火墙规则
FirewallRulerule= 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);
returnfalse;
}

// 5. 用户限制检查
if (!checkUserLimit(ipAddress, rule)) {
log.warn("IP {} 超过用户限制 {}", ipAddress, rule.getEffectiveUserLimit());
blockRequest(response, "访问次数超过限制,请稍后再试", 429);
logAccess(ipAddress, apiPath, userAgent, method, 429, "用户限制拦截", startTime);
returnfalse;
}

// 6. 记录正常访问
logAccess(ipAddress, apiPath, userAgent, method, 200, null, startTime);
log.debug("IP {} 访问 {} 通过防火墙检查", ipAddress, apiPath);
returntrue;

} catch (Exception e) {
log.error("防火墙拦截器处理异常: IP={}, API={}", ipAddress, apiPath, e);
logAccess(ipAddress, apiPath, userAgent, method, 500, "系统异常", startTime);
returntrue; // 异常情况下允许通过,避免影响正常业务
}
}

/**
* 检查QPS限制
*/
privatebooleancheckQpsLimit(String ipAddress, String apiPath, FirewallRule rule) {
Stringkey= ipAddress + ":" + apiPath;
intqpsLimit= rule.getEffectiveQpsLimit();

if (qpsLimit <= 0) {
returntrue; // 无限制
}

AtomicIntegercounter= qpsCache.getIfPresent(key);
if (counter == null) {
counter = newAtomicInteger(0);
qpsCache.put(key, counter);
}

intcurrentCount= counter.incrementAndGet();
return currentCount <= qpsLimit;
}

/**
* 检查用户限制
*/
privatebooleancheckUserLimit(String ipAddress, FirewallRule rule) {
intuserLimit= rule.getEffectiveUserLimit();

if (userLimit <= 0) {
returntrue; // 无限制
}

AtomicIntegercounter= userCache.getIfPresent(ipAddress);
if (counter == null) {
counter = newAtomicInteger(0);
userCache.put(ipAddress, counter);
}

intcurrentCount= counter.incrementAndGet();
return currentCount <= userLimit;
}

/**
* 获取客户端真实IP地址
*/
private String getClientIpAddress(HttpServletRequest request) {
StringxForwardedFor= request.getHeader("X-Forwarded-For");
if (xForwardedFor != null && !xForwardedFor.isEmpty() && !"unknown".equalsIgnoreCase(xForwardedFor)) {
return xForwardedFor.split(",")[0].trim();
}

StringxRealIp= request.getHeader("X-Real-IP");
if (xRealIp != null && !xRealIp.isEmpty() && !"unknown".equalsIgnoreCase(xRealIp)) {
return xRealIp;
}

return request.getRemoteAddr();
}
}

4.4 在线配置接口

@Slf4j
@RestController
@RequestMapping("/api/firewall")
@CrossOrigin(origins = "*")// 允许跨域访问
publicclassFirewallController {

@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 = newHashMap<>();
try {
booleansuccess= 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 = newHashMap<>();
try {
booleansuccess= 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 = newHashMap<>();
try {
booleansuccess= 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,提供完整的防火墙管理功能:

<!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 功能

class FirewallApp {
constructor() {
this.apiBase = '/api/firewall';
this.currentPage = 'dashboard';
this.rules = [];
this.charts = {};
this.init();
}

asyncinit() {
this.bindEvents();
awaitthis.loadDashboard();
this.startAutoRefresh();
}

// 加载仪表盘数据
asyncloadDashboard() {
try {
const [rules, blacklist, statistics] = awaitPromise.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 = newChart(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
}
}
}
});
}

// 获取数据
asyncfetchData(endpoint) {
const response = awaitfetch(this.apiBase + endpoint);
if (!response.ok) {
thrownewError(`HTTP error! status: ${response.status}`);
}
returnawait response.json();
}

// 自动刷新
startAutoRefresh() {
setInterval(() => {
if (this.currentPage === 'dashboard') {
this.loadDashboard();
}
}, 30000); // 每30秒刷新一次
}
}

// 初始化应用
newFirewallApp();

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

6. 配置文件

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

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-ddHH: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 中小型系统防护

电商网站场景:

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

内容平台场景:

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

7.2 突发流量应对

营销活动期间:

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

爬虫防护:

// 手动添加黑名单示例
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 部署:

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

监控集成:

# Prometheus 监控指标
management:
metrics:
tags:
application: firewall
export:
prometheus:
enabled: true

总结

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

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

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

https://github.com/yuboon/java-examples/tree/master/springboot-firewall

文章目录
  1. 1. 1. 背景与痛点
  2. 2. 2. 设计思路
  3. 3. 3. 技术选型
  4. 4. 4. 核心实现
    1. 4.0.1. 4.1 定义规则实体
    2. 4.0.2. 4.2 规则管理器
    3. 4.0.3. 4.3 拦截器实现逻辑
    4. 4.0.4. 4.4 在线配置接口
  • 5. 5. 前端控制台
    1. 5.0.1. 核心 JavaScript 功能
  • 6. 6. 配置文件
  • 7. 7. 实战应用场景
    1. 7.0.1. 7.1 中小型系统防护
    2. 7.0.2. 7.2 突发流量应对
    3. 7.0.3. 7.3 部署和运维
  • 8. 总结