Rust网络编程库client-ip的使用:轻松获取客户端真实IP地址的高效工具

以下是关于Rust网络编程库client-ip的使用介绍和完整示例:

Rust网络编程库client-ip的使用:轻松获取客户端真实IP地址的高效工具

client-ip是一个从http::HeaderMap中安全提取客户端IP的Rust库。该代码最初是从axum-client-ip crate中提取出来的,用于非axum的使用场景。

支持的提取器

以下表格列出了当前支持的IP提取器及其对应的HTTP头:

提取器 使用的头 典型的代理/服务
cf_connecting_ip CF-Connecting-IP Cloudflare
cloudfront_viewer_address CloudFront-Viewer-Address AWS CloudFront
fly_client_ip Fly-Client-IP Fly.io
rightmost_forwarded Forwarded 支持RFC 7239的代理(提取最右边的for=)
rightmost_x_forwarded_for X-Forwarded-For Nginx, Apache, HAProxy, CDNs, LBs
true_client_ip True-Client-IP Cloudflare, Akamai
x_real_ip X-Real-Ip Nginx

安装

在项目目录中运行以下Cargo命令:

cargo add client-ip

或者在Cargo.toml中添加以下行:

client-ip = "0.1.1"

使用示例

下面是内容中提供的示例代码:

use client_ip::*;
use http::HeaderMap;

fn main() {
    // 创建模拟的HTTP请求头
    let mut headers = HeaderMap::new();
    
    // 添加各种可能的IP头
    headers.insert("CF-Connecting-IP", "203.0.113.1".parse().unwrap());
    headers.insert("X-Forwarded-For", "203.0.113.2, 198.51.100.1".parse().unwrap());
    headers.insert("X-Real-Ip", "203.0.113.3".parse().unwrap());
    
    // 尝试从不同头中提取IP
    let ip_from_cloudflare = cf_connecting_ip(&headers);
    let ip_from_x_forwarded = rightmost_x_forwarded_for(&headers);
    let ip_from_x_real = x_real_ip(&headers);
    
    println!("IP from Cloudflare header: {:?}", ip_from_cloudflare);
    println!("IP from X-Forwarded-For: {:?}", ip_from_x_forwarded);
    println!("IP from X-Real-Ip: {:?}", ip_from_x_real);
    
    // 优先使用特定代理的头,然后尝试其他头
    let client_ip = cf_connecting_ip(&headers)
        .or_else(|| rightmost_x_forwarded_for(&headers))
        .or_else(|| x_real_ip(&headers))
        .unwrap_or_else(|| std::net::IpAddr::from([0, 0, 0, 0]));
    
    println!("最终确定的客户端IP: {}", client_ip);
}

完整示例demo

以下是一个更完整的示例,展示如何在Web服务器中使用client-ip:

use client_ip::*;
use http::{HeaderMap, Request, Response};
use hyper::Body;

async fn handle_request(req: Request<Body>) -> Result<Response<Body>, hyper::Error> {
    // 从请求中获取头信息
    let headers = req.headers();
    
    // 尝试从不同头中提取客户端IP
    let client_ip = cf_connecting_ip(&headers)
        .or_else(|| rightmost_x_forwarded_for(&headers))
        .or_else(|| x_real_ip(&headers))
        .unwrap_or_else(|| std::net::IpAddr::from([0, 0, 0, 0]));
    
    println!("客户端IP: {}", client_ip);
    
    // 创建响应
    let response = Response::builder()
        .status(200)
        .body(Body::from(format!("你的IP地址是: {}", client_ip)))
        .unwrap();
    
    Ok(response)
}

#[tokio::main]
async fn main() {
    // 创建模拟请求
    let mut request = Request::builder()
        .uri("http://example.com")
        .body(Body::empty())
        .unwrap();
    
    // 添加测试头
    request.headers_mut().insert(
        "X-Forwarded-For", 
        "203.0.113.1, 198.51.100.1".parse().unwrap()
    );
    
    // 处理请求
    let response = handle_request(request).await.unwrap();
    
    // 打印响应体
    let body = hyper::body::to_bytes(response.into_body()).await.unwrap();
    println!("服务器响应: {}", String::from_utf8(body.to_vec()).unwrap());
}

贡献

如果你维护其他基于http框架的客户端IP提取功能,可以考虑使用这个crate,这样我们就可以在一个地方处理安全敏感的代码。

许可证

该项目采用MIT许可证。


1 回复

Rust网络编程库client-ip使用指南

client-ip是一个用于获取客户端真实IP地址的Rust库,特别适合在反向代理或负载均衡环境下使用。

功能特点

  • 支持从多种HTTP头中提取客户端IP
  • 自动处理X-Forwarded-For、Forwarded等标准头部
  • 提供灵活的配置选项
  • 高性能,零成本抽象

基本使用方法

添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
client-ip = "0.2"

基本示例

use client_ip::ClientIp;
use std::net::SocketAddr;

fn main() {
    // 模拟从请求中获取的信息
    let headers = vec![
        ("x-forwarded-for", "203.0.113.45, 198.51.100.67"),
        ("forwarded", "for=192.0.2.43;proto=http"),
    ];
    
    let remote_addr: SocketAddr = "198.51.100.67:12345".parse().unwrap();
    
    // 获取客户端IP
    let client_ip = ClientIp::new(&headers, Some(&remote_addr)).get_ip();
    
    println!("客户端真实IP: {}", client_ip.unwrap());
    // 输出: 203.0.113.45
}

高级配置

自定义信任代理

use client_ip::{ClientIp, ClientIpConfig};
use std::net::{IpAddr, Ipv4Addr};

fn main() {
    let config = ClientIpConfig::new()
        .trust_proxy(vec![
            IpAddr::V4(Ipv4Addr::new(198, 51, 100, 0)),
            IpAddr::V4(Ipv4Addr::new(203, 0, 113, 0)),
        ])
        .enable_forwarded_header(true);
    
    let headers = vec![
        ("x-forwarded-for", "203.0.113.45, 198.51.100.67"),
    ];
    
    let remote_addr: SocketAddr = "198.51.100.67:12345".parse().unwrap();
    
    let client_ip = ClientIp::with_config(&headers, Some(&remote_addr), config)
        .get_ip();
    
    println!("客户端真实IP: {}", client_ip.unwrap());
}

与Web框架集成示例

使用Actix-web

use actix_web::{web, App, HttpServer, HttpRequest, Responder};
use client_ip::ClientIp;

async fn index(req: HttpRequest) -> impl Responder {
    let headers: Vec<_> = req.headers()
        .iter()
        .map(|(name, value)| (name.as_str(), value.to_str().unwrap()))
        .collect();
    
    let remote_addr = req.peer_addr();
    
    let client_ip = ClientIp::new(&headers, remote_addr.as_ref())
        .get_ip()
        .unwrap_or_else(|| "未知IP".to_string());
    
    format!("你的IP地址是: {}", client_ip)
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new().route("/", web::get().to(index))
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

使用Warp框架

use warp::Filter;
use client_ip::ClientIp;
use std::net::SocketAddr;

async fn get_ip(headers: Vec<(&str, &str)>, remote_addr: Option<SocketAddr>) -> String {
    let client_ip = ClientIp::new(&headers, remote_addr.as_ref())
        .get_ip()
        .unwrap_or_else(|| "未知IP".to_string());
    
    format!("你的IP地址是: {}", client_ip)
}

#[tokio::main]
async fn main() {
    let route = warp::any()
        .and(warp::header::headers_cloned())
        .and(warp::addr::remote())
        .then(|headers: warp::http::HeaderMap, addr: Option<SocketAddr>| async move {
            let headers: Vec<_> = headers.iter()
                .map(|(name, value)| (name.as_str(), value.to_str().unwrap()))
                .collect();
            
            get_ip(headers, addr).await
        });
    
    warp::serve(route).run(([127, 0, 0, 1], 3030)).await;
}

注意事项

  1. 在反向代理环境下,确保代理服务器正确设置了X-Forwarded-For等头部
  2. 对于安全敏感应用,应该配置信任的代理IP列表
  3. 该库不会验证IP地址的真实性,只是按规则提取
  4. 在生产环境中,建议结合其他安全措施使用

client-ip库通过简洁的API提供了强大的客户端IP提取功能,是处理复杂网络环境下客户端识别的理想选择。

完整示例Demo

下面是一个结合Axum框架的完整示例:

use axum::{
    routing::get,
    Router,
    http::{HeaderMap, StatusCode},
    response::IntoResponse,
};
use client_ip::ClientIp;
use std::net::SocketAddr;

async fn ip_handler(headers: HeaderMap, remote_addr: Option<SocketAddr>) -> impl IntoResponse {
    // 转换HeaderMap为client-ip所需的格式
    let headers: Vec<_> = headers.iter()
        .map(|(name, value)| (name.as_str(), value.to_str().unwrap()))
        .collect();
    
    // 获取客户端IP
    let client_ip = ClientIp::new(&headers, remote_addr.as_ref())
        .get_ip()
        .unwrap_or_else(|| "未知IP".to_string());
    
    (StatusCode::OK, format!("你的IP地址是: {}", client_ip))
}

#[tokio::main]
async fn main() {
    // 构建路由
    let app = Router::new()
        .route("/", get(ip_handler));
    
    // 启动服务器
    let listener = tokio::net::TcpListener::bind("127.0.0.1:3000").await.unwrap();
    println!("服务器运行在 http://127.0.0.1:3000");
    
    axum::serve(listener, app).await.unwrap();
}

这个示例展示了如何:

  1. 创建一个简单的Axum Web服务器
  2. 处理HTTP请求中的headers
  3. 使用client-ip库提取客户端真实IP
  4. 返回包含IP地址的响应

要运行这个示例,需要在Cargo.toml中添加以下依赖:

[dependencies]
axum = "0.6"
client-ip = "0.2"
tokio = { version = "1.0", features = ["full"] }
回到顶部