You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在actix-web处理器或中间件中无响应断开恶意客户端连接

嘿,用panic来实现强制关闭套接字确实不是个好主意——不仅不优雅,还可能触发actix-web的全局错误处理逻辑,甚至在极端情况下影响服务的稳定性。我给你分享几个更合理、更可控的实现方式,完全满足你“直接关闭连接、不发送任何响应”的需求:

核心思路:直接操作底层TCP流

actix-web基于Tokio运行时,所有HTTP连接本质上都是TCP流。我们可以在中间件中获取到这个流,当检测到恶意客户端时直接关闭它,这样客户端会被强制断开,不会收到任何HTTP响应。

步骤1:将TCP流存入请求上下文

首先,我们需要在连接建立时把TCP流的引用存入请求的extensions中,这样后续中间件就能拿到它。在启动HttpServer时通过on_connect回调实现:

use actix_web::{HttpServer, App};
use actix_web::rt::net::TcpStream;

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(AntiDosMiddleware) // 后续会实现这个中间件
            // 注册你的路由...
    })
    .on_connect(|stream: &TcpStream, req_head: &mut actix_web::dev::RequestHead| {
        // 将TCP流克隆后存入请求的扩展字段
        req_head.extensions_mut().insert(stream.clone());
    })
    .bind(("0.0.0.0", 8080))?
    .run()
    .await
}

步骤2:实现抗DoS/暴力破解中间件

接下来编写中间件,在请求处理前检测客户端行为,一旦判定为恶意就直接关闭TCP流:

use actix_web::{
    dev::{Service, ServiceRequest, ServiceResponse, Transform},
    Error,
};
use futures_util::future::{ok, Ready};
use std::task::{Context, Poll};
use std::pin::Pin;
use actix_web::rt::net::TcpStream;
use log::error;

// 中间件结构体(无状态,也可以添加配置比如阈值、白名单等)
pub struct AntiDosMiddleware;

// 实现Transform trait,用于包装现有服务
impl<S, B> Transform<S, ServiceRequest> for AntiDosMiddleware
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Transform = AntiDosMiddlewareService<S>;
    type InitError = ();
    type Future = Ready<Result<Self::Transform, Self::InitError>>;

    fn new_transform(&self, service: S) -> Self::Future {
        ok(AntiDosMiddlewareService { service })
    }
}

// 中间件的服务实现
pub struct AntiDosMiddlewareService<S> {
    service: S,
}

impl<S, B> Service<ServiceRequest> for AntiDosMiddlewareService<S>
where
    S: Service<ServiceRequest, Response = ServiceResponse<B>, Error = Error>,
    S::Future: 'static,
    B: 'static,
{
    type Response = ServiceResponse<B>;
    type Error = Error;
    type Future = Pin<Box<dyn std::future::Future<Output = Result<Self::Response, Self::Error>>>>;

    fn poll_ready(&self, cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
        self.service.poll_ready(cx)
    }

    fn call(&self, req: ServiceRequest) -> Self::Future {
        // 1. 检测客户端是否为恶意(这里替换成你的实际检测逻辑)
        let is_malicious = self.check_malicious_client(&req);

        if is_malicious {
            // 2. 获取之前存入的TCP流
            if let Some(stream) = req.extensions().get::<TcpStream>() {
                let mut stream_clone = stream.clone();
                Box::pin(async move {
                    // 3. 关闭TCP流(读写方向都关闭)
                    if let Err(e) = stream_clone.shutdown(std::net::Shutdown::Both) {
                        error!("Failed to shutdown malicious client stream: {}", e);
                    }
                    // 返回一个连接中止的错误,让actix-web停止处理该请求
                    Err(Error::from(std::io::Error::new(
                        std::io::ErrorKind::ConnectionAborted,
                        "Malicious client blocked",
                    )))
                })
            } else {
                //  fallback:如果拿不到流,返回错误(这种情况很少见)
                Box::pin(async {
                    Err(Error::from(std::io::Error::new(
                        std::io::ErrorKind::Other,
                        "Failed to access TCP stream for blocking",
                    )))
                })
            }
        } else {
            // 正常请求,转发给后续服务处理
            let fut = self.service.call(req);
            Box::pin(async move { fut.await })
        }
    }

    // 示例检测逻辑:替换成你的实际规则(比如IP频率限制、请求特征匹配等)
    fn check_malicious_client(&self, req: &ServiceRequest) -> bool {
        // 示例:限制某个IP的请求频率,或者检测暴力破解特征
        let peer_addr = req.connection_info().peer_addr().unwrap_or("unknown");
        // 这里可以用Redis/内存计数器实现频率限制,比如1分钟内超过100次请求判定为恶意
        // 为了演示,这里简单判定来自127.0.0.1的请求为恶意
        peer_addr.starts_with("127.0.0.1:")
    }
}

关键注意事项

  • 检测逻辑要高效:如果是高频请求场景,避免用同步阻塞的操作(比如同步Redis调用),尽量用异步API或者无锁内存结构(比如dashmap)来做频率统计,防止中间件成为性能瓶颈。
  • 添加白名单机制:避免误杀合法的爬虫、内部服务等,可以在中间件中加入白名单IP/域名的判断。
  • 日志记录:关闭连接时记录日志,方便后续排查恶意行为的来源。
  • 避免panic:panic会导致actix-web的工作线程处理错误,甚至可能触发线程重启,影响服务可用性。上面的实现完全用错误返回替代了panic,更可控。

内容的提问来源于stack exchange,提问作者kyku

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.28 11:32:39