UserInfo 提取器详解
大约 5 分钟
UserInfo
提取器详解
UserInfo
是我们应用中用于处理用户身份验证的核心工具。它不仅仅是一个简单的数据结构,更是一个强大的 Axum 提取器 (Extractor)。
它的主要目标是:将身份验证的复杂逻辑从业务处理函数 (Handler) 中完全剥离,让我们的代码更简洁、更安全、更易于维护。
核心理念
如果你在函数签名中请求了 UserInfo
,那么在你的函数代码执行时,可以 100% 保证 这个用户是已经通过身份验证的合法用户。你无需再进行任何手动检查。
结构体定义
首先,我们来看看 UserInfo
结构体的具体定义。它包含了从 Token 中解析出的当前登录用户的核心信息。
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct UserInfo {
/// 用户名
pub username: String,
/// 用户唯一 ID (User ID)
pub uid: i64,
/// 设备 ID (Device ID)
pub did: i64,
/// 角色 ID (Role ID)
pub rid: i64,
/// 当前 Token 的唯一标识 ID
pub token_id: i64,
}
#[derive]
宏的解释
你可能注意到了结构体上方的 #[derive(...)]
宏,它为我们自动实现了一些非常有用的特性 (Trait):
点击展开查看每个 Trait 的作用
Debug
: 允许我们使用{:?}
格式化符打印UserInfo
实例,方便调试。例如println!("{:?}", user_info);
。Clone
: 允许我们创建UserInfo
的一个完整副本。这在需要传递所有权但又想保留原始实例时非常有用。Default
: 允许我们创建一个默认的、空的UserInfo
实例(数值为0
,字符串为空)。主要用于测试或初始化场景。Serialize
,Deserialize
: 这是由serde
库提供的能力,至关重要。Serialize
: 允许我们将UserInfo
结构体 序列化 成 JSON 字符串,方便作为 API 响应返回给前端。Deserialize
: 允许我们将 JSON 字符串 反序列化 成UserInfo
结构体。
如何使用
在需要身份验证的 Axum Handler 中使用 UserInfo
非常简单:只需将其作为函数的参数即可。
Axum 的提取器系统会自动处理剩下的所有事情。
✅ 推荐做法
use crate::extractors::UserInfo; // 确保引入了 UserInfo
/// 获取当前用户的个人资料
async fn get_my_profile(
// 只需添加这一行!
// Axum 会自动运行我们的验证逻辑。
// 如果验证失败,请求会直接被拒绝,根本不会执行这个函数的代码。
user_info: UserInfo,
) -> impl IntoResponse {
// 在这里,你可以完全信任 user_info 是一个合法的、已登录的用户
println!("获取用户 {} (uid: {}) 的资料", user_info.username, user_info.uid);
// 直接将获取到的用户信息作为 JSON 返回
ApiResponse::ok(user_info)
}
❌ 之前的做法 (对比)
为了让你更直观地感受到 UserInfo
提取器带来的便利,我们来看看如果没有它,代码会是什么样子:
// 这是一个不使用提取器的例子,逻辑混乱且重复
async fn get_my_profile_the_hard_way(
State(state): State<AppState>,
headers: HeaderMap,
) -> Result<Json<UserInfo>, StatusCode> {
// 1. 手动从请求头获取 Token
let token = headers.get("Authorization")
.and_then(|v| v.to_str().ok())
.and_then(|s| s.strip_prefix("Bearer "))
.ok_or(StatusCode::UNAUTHORIZED)?;
// 2. 调用服务验证 Token 并查询用户信息
let user_info = state.auth_service.validate_and_get_user(token).await
.map_err(|_| StatusCode::UNAUTHORIZED)?;
// 3. 终于可以开始写真正的业务逻辑了...
Ok(Json(user_info))
}
对比之下,UserInfo
提取器的优势一目了然。
核心优势
- 代码简洁 (Clean Code): 将 Handler 从繁琐的认证逻辑中解放出来,使其只专注于核心业务。
- 职责分离 (Separation of Concerns): 认证逻辑被统一封装在提取器内部,业务逻辑在 Handler 内部,二者互不干扰。
- 安全可靠 (Secure by Default): 从根本上避免了忘记在 Handler 中添加认证检查的风险。只要函数签名里有
UserInfo
,它就是受保护的。 - 易于维护 (Easy to Maintain): 如果未来认证逻辑需要变更(例如更换 Token 验证库),我们只需要修改
UserInfo
提取器一个地方,所有使用它的接口都会自动更新。 - 类型安全 (Type-Safe): 你得到的是一个完整的、强类型的
UserInfo
结构体,而不是一堆零散的字符串或数字,IDE 也能提供完美的自动补全支持。
深入原理:它是如何工作的?
UserInfo
实现了 Axum 的 FromRequestParts
Trait,这让它成为了一个合法的提取器。当 Axum 准备调用你的 Handler 时,它会检查每个参数:
- 当看到
userinfo: UserInfo
参数时,它会调用UserInfo
的提取逻辑。 - 该逻辑会自动从请求头中解析出
Authorization: Bearer <token>
。 - 如果 Token 不存在或格式不正确,提取器会立即返回
401 Unauthorized
错误,请求被中断。 - 如果 Token 存在,提取器会使用它来查询数据库或调用认证服务,验证其有效性并获取用户信息。
- 如果 Token 无效、已过期或找不到对应用户,提取器同样返回
401 Unauthorized
错误。 - 只有在所有验证都成功后,提取器才会将查询到的用户信息构造成一个
UserInfo
实例,并将其注入到你的 Handler 参数中,然后你的 Handler 代码才开始执行。