Rust实现局域网设备IP发现:优先返回200响应并验证JWT
Rust局域网设备IP发现与JWT验证实现方案
需求梳理
你需要实现一个局域网扫描函数,完成以下目标:
- 对
192.168.0.0/24网段的所有IP发送GET http://{ip}:8000/ping请求 - 一旦收到状态码200的响应,立即返回该IP并终止所有未完成的请求
- 验证响应返回的JWT令牌,确保目标设备合法
现有代码问题
当前代码仅异步spawn了所有请求,但没有处理响应、没有等待任务完成,也没有实现终止机制和JWT验证逻辑。我们需要基于tokio和reqwest完善这些功能,同时引入JWT验证库。
实现步骤与完整代码
1. 添加依赖
首先在Cargo.toml中添加所需依赖:
[dependencies] tokio = { version = "1.0", features = ["full"] } reqwest = { version = "0.11", features = ["json"] } jsonwebtoken = "9.0" serde = { version = "1.0", features = ["derive"] } thiserror = "1.0"
2. 完整实现代码
use jsonwebtoken::{decode, DecodingKey, Validation}; use reqwest::Client; use serde::Deserialize; use thiserror::Error; use tokio::task::JoinSet; // 定义自定义错误类型 #[derive(Error, Debug)] pub enum DiscoveryError { #[error("HTTP请求错误: {0}")] ReqwestError(#[from] reqwest::Error), #[error("JWT验证失败: {0}")] JwtError(#[from] jsonwebtoken::errors::Error), #[error("未找到合法设备")] NoDeviceFound, } // 定义JWT的Payload结构,根据实际需求调整字段 #[derive(Debug, Deserialize)] struct JwtPayload { // 示例:设备标识,可根据实际情况添加其他声明(如exp过期时间) device_id: String, exp: usize, // 过期时间戳,建议验证该字段 } async fn discover() -> Result<String, DiscoveryError> { // 创建可复用的HTTP客户端,避免重复建立连接 let client = Client::new(); // 管理所有异步任务,支持取消未完成任务 let mut tasks = JoinSet::new(); // 遍历网段内所有IP for i in 0..256 { let client = client.clone(); tasks.spawn(async move { let ip = format!("192.168.0.{i}"); let url = format!("http://{ip}:8000/ping"); // 发送请求,设置超时避免长时间等待 match client.get(&url).timeout(std::time::Duration::from_secs(2)).await { Ok(response) => { if response.status().is_success() { // 读取响应体中的JWT令牌 let jwt_token = response.text().await?; // 验证JWT validate_jwt(&jwt_token)?; Ok(ip) } else { Err(DiscoveryError::NoDeviceFound) } } Err(_) => Err(DiscoveryError::NoDeviceFound), } }); } // 等待任务完成,一旦找到合法设备就终止所有任务 while let Some(result) = tasks.join_next().await { match result { Ok(Ok(ip)) => { // 取消所有未完成的任务 tasks.shutdown().await; return Ok(ip); } // 忽略单个任务的错误,继续等待其他任务 _ => continue, } } // 所有任务完成都未找到合法设备 Err(DiscoveryError::NoDeviceFound) } // JWT验证函数,需替换为实际的秘钥和验证规则 fn validate_jwt(token: &str) -> Result<(), jsonwebtoken::errors::Error> { // 替换为你的实际秘钥,建议从环境变量读取,不要硬编码 let secret = b"your-secret-key-here"; let decoding_key = DecodingKey::from_secret(secret); // 配置验证规则,默认会验证exp字段 let validation = Validation::default(); // 解码并验证JWT let _payload: JwtPayload = decode(token, &decoding_key, &validation)?; // 如果需要额外验证逻辑(比如device_id是否符合预期),可以在这里添加 // if payload.device_id != "expected-device-id" { // return Err(jsonwebtoken::errors::Error::InvalidToken); // } Ok(()) } #[tokio::main] async fn main() { match discover().await { Ok(ip) => println!("找到合法设备: {}", ip), Err(e) => println!("发现失败: {}", e), } }
关键实现细节
- 并发任务管理:使用
tokio::task::JoinSet来管理所有扫描任务,一旦找到符合条件的设备,调用shutdown()立即终止所有未完成的任务,避免不必要的网络请求。 - 请求超时:给每个HTTP请求设置超时(示例中为2秒),防止单个无响应的IP阻塞整个扫描流程。
- JWT验证:通过
jsonwebtoken库验证令牌的签名和声明(默认验证exp过期时间),确保返回令牌的合法性,防范伪造设备或中间人攻击。 - 错误处理:自定义错误类型,清晰区分不同类型的错误,便于调试和处理。
内容的提问来源于stack exchange,提问作者Murad
相关产品推荐
相关产品推荐

