Web 應用常面臨:惡意刷單、暴力破解、CC 攻擊 (高併發請求耗盡資源)、爬蟲盜刷資料。硬體防火牆/ WAF (Web Application Firewall) 雖強,但成本高、規則通用、難以感知業務語義。**應用層軟體檢測** 可精準識別「同一用戶/ IP 短時間高頻訪問」、「登入失敗超限」、「異常 User-Agent」等業務異常,配合攔截、驗證碼、封禁、降級等動態響應,是纏深防禦體系的關鍵一環。
Java Web 兩大架構下的切入點:
javax.servlet.Filter,在 web.xml 或 @WebFilter 配置 /*,**確保位於 Filter 鏈最前端** (Spring Boot 可用 @Order(Ordered.HIGHEST_PRECEDENCE))。DispatcherServlet, ActionServlet) 內部攔截器 / 攔截器鏈 / 核心 Controller 執行前調用檢測邏輯。UserConnect@Data
public class UserConnect {
private String userName; // 用戶名/匿名標識
private String ip; // 客戶端 IP (注意反向代理下取 X-Forwarded-For)
private LocalDateTime firstFailureTime; // 首次異常時間
private LocalDateTime lastAccessTime; // 最近訪問時間
private int failureCount; // 異常/高頻計數
private int status = 0; // 0 正常, -1 封禁
}
UserConnectManage (單例/執行緒安全)@Component
public class UserConnectManage {
// 配置參數 (支援動態刷新 @ConfigurationProperties)
private int maxFailureTimes = 10; // 最大允許異常次數
private long maxFailureIntervalMs = 10_000; // 統計時間窗口 (毫秒)
private long waitIntervalMs = 60_000; // 封禁冷卻時間
private int maxOnlineUsers = 10_000; // 記憶體保護上限
// 核心儲存:ConcurrentHashMap 高併發安全
private final Map userMap = new ConcurrentHashMap<>();
// 定時清理任務 (每 2 秒掃描)
@Scheduled(fixedRate = 2000)
public void cleanExpired() {
long now = System.currentTimeMillis();
userMap.entrySet().removeIf(entry -> {
UserConnect uc = entry.getValue();
// 清理超時記錄
if (now - uc.getFirstFailureTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli() > maxFailureIntervalMs) {
return true;
}
// 記憶體保護:超限隨機淘汰 50%
if (userMap.size() > maxOnlineUsers) {
// 簡單策略:隨機清理一半 (生產建議用 LRU/Caffeine)
}
return false;
});
}
/**
* 核心校驗入口:返回 true 放行,false 攔截
*/
public synchronized boolean checkLoginValidate(String ip, String userName) {
String key = ip + ":" + userName;
UserConnect uc = userMap.computeIfAbsent(key, k -> {
UserConnect n = new UserConnect();
n.setIp(ip); n.setUserName(userName);
n.setFirstFailureTime(LocalDateTime.now());
n.setFailureCount(0);
return n;
});
long nowMs = System.currentTimeMillis();
long firstMs = uc.getFirstFailureTime().toInstant(ZoneOffset.ofHours(8)).toEpochMilli();
// 已封禁且未過冷卻期
if (uc.getStatus() == -1 && (nowMs - firstMs < maxFailureIntervalMs + waitIntervalMs)) {
return false;
}
// 封禁期過,重置狀態
if (uc.getStatus() == -1) {
uc.setStatus(0);
uc.setFailureCount(0);
uc.setFirstFailureTime(LocalDateTime.now());
}
// 計數+1
uc.setFailureCount(uc.getFailureCount() + 1);
uc.setLastAccessTime(LocalDateTime.now());
// 判斷是否超限
if (uc.getFailureCount() > maxFailureTimes && (nowMs - firstMs < maxFailureIntervalMs)) {
uc.setStatus(-1); // 封禁
return false;
}
return true;
}
// 管理介面:重置/移除/清空/動態調參...
}
@WebFilter(urlPatterns = "/*", dispatcherTypes = {DispatcherType.REQUEST, DispatcherType.FORWARD})
@Order(Ordered.HIGHEST_PRECEDENCE)
public class IntrusionDetectionFilter implements Filter {
@Autowired private UserConnectManage detector;
@Override
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) resp;
String ip = getRealIp(request); // 處理 Nginx 反代頭
String user = Optional.ofNullable(request.getRemoteUser()).orElse("anonymous");
if (!detector.checkLoginValidate(ip, user)) {
response.setContentType("application/json;charset=UTF-8");
response.setStatus(HttpServletResponse.SC_TOO_MANY_REQUESTS);
response.getWriter().write(
"{\"code\":429,\"msg\":\"訪問過於頻繁,請 " + detector.getWaitIntervalMs()/1000 + " 秒後再試\"}"
);
return; // 直接截斷鏈
}
chain.doFilter(request, response); // 放行
}
private String getRealIp(HttpServletRequest request) {
String ip = request.getHeader("X-Forwarded-For");
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getHeader("X-Real-IP");
}
if (ip == null || ip.isEmpty() || "unknown".equalsIgnoreCase(ip)) {
ip = request.getRemoteAddr();
}
return ip.split(",")[0].trim(); // 取第一個非代理 IP
}
}
| 痛點 | 進階方案 |
|---|---|
| 單機記憶體限制、重啟丟失 | **Redis + Lua 腳本** 原子操作計數器、滑動窗口、分散式鎖;鍵設計 ratelimit:{ip}:{api},TTL 自動過期。 |
| 固定窗口邊界突刺 | **滑動窗口 / 滑動日誌 / 令牌桶 / 漏桶** 演算法 (Guava RateLimiter、Resilience4j、Sentinel)。 |
| 僅限流無風險識別 | 整合 **設備指紋 (FP)**、行為分析 (滑鼠軌跡、點擊間隔)、威脅情報庫 (惡意 IP 庫)、驗證碼 (圖形/滑塊/行為) 多因子驗證。 |
| 誤傷正常用戶 | 白名單機制 (內部 IP、合作夥伴)、分級策略 (登入/支付/查詢不同閾值)、優雅降級 (返回靜態頁/快取而非硬拒)。 |
| 可觀測性缺失 | 埋點 Metrics (QPS、拒絕率、封禁數)、Grafana 儀表板、告警規則 (連續 5 分鐘拒絕率 > 20% 觸發 P0)。 |
客戶端 → [CDN/WAF] → [Nginx 限流/黑白名單] → [API Gateway 限流/鑒權]
→ [應用層 Filter 入侵檢測/業務風控] → [業務邏輯] → [資料庫/快取]
各層職責不同、顆粒度遞增,共同構建高可用、可演進的安全防線。