如何在Rust中临时存储Bearer Token并设置900秒过期
Rust中Bearer Token的临时存储与过期管理实现
核心思路
实现一个带过期校验的token缓存,核心能力包括:
- 存储token字符串与对应过期时间戳
- 自动判断token有效性,过期则触发重新请求
- 保证异步环境下的线程安全访问
实现步骤
1. 定义全局异步安全的Token缓存
使用Arc<Mutex<>>实现跨任务的安全共享,结合lazy_static创建全局静态缓存实例:
use std::sync::{Arc, Mutex}; use std::time::{SystemTime, UNIX_EPOCH}; use lazy_static::lazy_static; #[derive(Debug, Clone)] struct TokenCache { access_token: Option<String>, expires_at: u64, // 过期时间的UNIX时间戳(秒) } impl TokenCache { fn new() -> Self { TokenCache { access_token: None, expires_at: 0, } } // 检查token是否未过期 fn is_valid(&self) -> bool { let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); now < self.expires_at } } // 全局静态缓存实例 lazy_static! { static ref TOKEN_CACHE: Arc<Mutex<TokenCache>> = Arc::new(Mutex::new(TokenCache::new())); }
2. 封装统一的有效Token获取函数
将token请求、缓存更新、过期校验逻辑封装到一个函数中,其他业务代码直接调用即可:
use reqwest::{Client, StatusCode}; use serde::Deserialize; use std::collections::HashMap; #[derive(Debug, Deserialize)] struct SecureToken { access_token: String, expires_in: u32, // 接口返回的token有效期(秒),如果是直接返回过期时间戳可替换为expiration_date_time } #[derive(Debug, Deserialize)] struct BadRequest { error: String, error_description: Option<String>, } async fn get_valid_token() -> Result<String, Box<dyn std::error::Error>> { let mut cache = TOKEN_CACHE.lock().unwrap(); // 缓存有效直接返回 if cache.is_valid() { return Ok(cache.access_token.as_ref().unwrap().clone()); } // 缓存失效,请求新token let client = Client::new(); let mut map = HashMap::new(); map.insert("client_id", "your_client_id"); map.insert("client_secret", "your_client_secret"); map.insert("scope", "your_scope"); map.insert("grant_type", "client_credentials"); let token_request = client .post("https://gatway.address") .form(&map) .send() .await?; match token_request.status() { StatusCode::OK => { let secure_token = token_request.json::<SecureToken>().await?; let now = SystemTime::now() .duration_since(UNIX_EPOCH) .unwrap() .as_secs(); // 提前10秒刷新,避免刚好过期的边界情况 let expires_at = now + secure_token.expires_in as u64 - 10; // 更新缓存 cache.access_token = Some(secure_token.access_token.clone()); cache.expires_at = expires_at; Ok(secure_token.access_token) } StatusCode::FORBIDDEN => Err("无法连接到认证服务".into()), StatusCode::BAD_REQUEST => { let bad_request = token_request.json::<BadRequest>().await?; Err(format!("请求错误: {:?}", bad_request).into()) } s => Err(format!("未知状态码: {:?}", s).into()), } }
3. 业务代码调用示例
在需要使用token的接口请求中,直接调用get_valid_token()即可:
async fn call_protected_api() -> Result<(), Box<dyn std::error::Error>> { let token = get_valid_token().await?; let client = Client::new(); let response = client .get("https://api.example.com/protected") .bearer_auth(token) .send() .await?; println!("API响应状态: {:?}", response.status()); Ok(()) } #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // 首次调用会请求新token call_protected_api().await?; // 短时间内再次调用使用缓存token call_protected_api().await?; Ok(()) }
依赖配置
需要在Cargo.toml中添加以下依赖:
[dependencies] reqwest = { version = "0.11", features = ["json"] } serde = { version = "1.0", features = ["derive"] } lazy_static = "1.4" tokio = { version = "1.0", features = ["full"] }
注意事项
- 如果接口返回的是直接的过期时间戳(
expiration_date_time),则无需计算,直接将expires_at设为该值即可 Arc<Mutex<>>确保异步任务间的缓存访问安全,避免数据竞争- 提前10秒刷新的逻辑可根据业务需求调整,避免在请求时刻刚好遇到token过期
内容的提问来源于stack exchange,提问作者LIDAR_Freak
相关产品推荐
相关产品推荐

