用户认证系统 #
本节将实现一个完整的用户认证系统,包括注册、登录、JWT认证和权限控制。
项目结构 #
text
auth_system/
├── Cargo.toml
├── Rocket.toml
├── src/
│ ├── main.rs
│ ├── auth/
│ │ ├── mod.rs
│ │ ├── jwt.rs
│ │ └── password.rs
│ ├── guards/
│ │ └── auth.rs
│ ├── models/
│ │ └── user.rs
│ └── routes/
│ └── auth.rs
依赖配置 #
toml
[dependencies]
rocket = { version = "0.5", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
jsonwebtoken = "9.2"
argon2 = "0.5"
rand = "0.8"
uuid = { version = "1.0", features = ["v4", "serde"] }
chrono = { version = "0.4", features = ["serde"] }
密码哈希 #
rust
use argon2::{
password_hash::{rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString},
Argon2,
};
pub fn hash_password(password: &str) -> Result<String, String> {
let salt = SaltString::generate(&mut OsRng);
Argon2::default()
.hash_password(password.as_bytes(), &salt)
.map(|h| h.to_string())
.map_err(|e| e.to_string())
}
pub fn verify_password(password: &str, hash: &str) -> bool {
let parsed = match PasswordHash::new(hash) {
Ok(p) => p,
Err(_) => return false,
};
Argon2::default()
.verify_password(password.as_bytes(), &parsed)
.is_ok()
}
JWT处理 #
rust
use serde::{Serialize, Deserialize};
use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};
#[derive(Debug, Serialize, Deserialize)]
pub struct Claims {
pub sub: String,
pub username: String,
pub role: String,
pub exp: usize,
}
pub struct JwtConfig {
secret: String,
expiration: u64,
}
impl JwtConfig {
pub fn new(secret: String, expiration_hours: u64) -> Self {
Self {
secret,
expiration: expiration_hours * 3600,
}
}
pub fn generate(&self, user_id: &str, username: &str, role: &str) -> Result<String, String> {
let exp = chrono::Utc::now()
.checked_add_signed(chrono::Duration::seconds(self.expiration as i64))
.unwrap()
.timestamp() as usize;
let claims = Claims {
sub: user_id.to_string(),
username: username.to_string(),
role: role.to_string(),
exp,
};
encode(
&Header::default(),
&claims,
&EncodingKey::from_secret(self.secret.as_bytes()),
).map_err(|e| e.to_string())
}
pub fn validate(&self, token: &str) -> Result<Claims, String> {
decode::<Claims>(
token,
&DecodingKey::from_secret(self.secret.as_bytes()),
&Validation::default(),
)
.map(|d| d.claims)
.map_err(|e| e.to_string())
}
}
认证守卫 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
use crate::auth::jwt::{JwtConfig, Claims};
pub struct AuthUser {
pub id: String,
pub username: String,
pub role: String,
}
pub struct AdminUser(pub AuthUser);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthUser {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let config = request.guard::<&rocket::State<JwtConfig>>()
.await
.unwrap();
let token = request.headers()
.get_one("Authorization")
.and_then(|h| h.strip_prefix("Bearer "));
match token.and_then(|t| config.validate(t).ok()) {
Some(claims) => Outcome::Success(AuthUser {
id: claims.sub,
username: claims.username,
role: claims.role,
}),
None => Outcome::Error((Status::Unauthorized, ())),
}
}
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let user = AuthUser::from_request(request).await;
match user {
Outcome::Success(u) if u.role == "admin" => Outcome::Success(AdminUser(u)),
Outcome::Success(_) => Outcome::Error((Status::Forbidden, ())),
Outcome::Error(e) => Outcome::Error(e),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
用户模型 #
rust
use serde::{Serialize, Deserialize};
use uuid::Uuid;
#[derive(Debug, Clone, Serialize)]
pub struct User {
pub id: Uuid,
pub username: String,
pub email: String,
pub role: String,
}
#[derive(Debug, Deserialize)]
pub struct RegisterRequest {
pub username: String,
pub email: String,
pub password: String,
}
#[derive(Debug, Deserialize)]
pub struct LoginRequest {
pub username: String,
pub password: String,
}
#[derive(Debug, Serialize)]
pub struct AuthResponse {
pub token: String,
pub user: User,
}
认证路由 #
rust
use rocket::serde::json::Json;
use rocket::State;
use rocket::http::Status;
use crate::models::{User, RegisterRequest, LoginRequest, AuthResponse};
use crate::auth::{jwt::JwtConfig, password::{hash_password, verify_password}};
use crate::guards::AuthUser;
use uuid::Uuid;
use std::collections::HashMap;
use std::sync::Mutex;
struct UserStore {
users: Mutex<HashMap<String, StoredUser>>,
}
struct StoredUser {
id: Uuid,
username: String,
email: String,
password_hash: String,
role: String,
}
#[post("/register", format = "json", data = "<req>")]
fn register(
req: Json<RegisterRequest>,
store: &State<UserStore>,
jwt: &State<JwtConfig>,
) -> Result<Json<AuthResponse>, Status> {
let password_hash = hash_password(&req.password)
.map_err(|_| Status::InternalServerError)?;
let id = Uuid::new_v4();
let user = StoredUser {
id,
username: req.username.clone(),
email: req.email.clone(),
password_hash,
role: "user".to_string(),
};
let token = jwt.generate(&id.to_string(), &user.username, &user.role)
.map_err(|_| Status::InternalServerError)?;
store.users.lock().unwrap().insert(user.username.clone(), user.clone());
Ok(Json(AuthResponse {
token,
user: User {
id: user.id,
username: user.username,
email: user.email,
role: user.role,
},
}))
}
#[post("/login", format = "json", data = "<req>")]
fn login(
req: Json<LoginRequest>,
store: &State<UserStore>,
jwt: &State<JwtConfig>,
) -> Result<Json<AuthResponse>, Status> {
let users = store.users.lock().unwrap();
let user = users.get(&req.username)
.ok_or(Status::Unauthorized)?;
if !verify_password(&req.password, &user.password_hash) {
return Err(Status::Unauthorized);
}
let token = jwt.generate(&user.id.to_string(), &user.username, &user.role)
.map_err(|_| Status::InternalServerError)?;
Ok(Json(AuthResponse {
token,
user: User {
id: user.id,
username: user.username.clone(),
email: user.email.clone(),
role: user.role.clone(),
},
}))
}
#[get("/profile")]
fn profile(user: AuthUser) -> Json<User> {
Json(User {
id: Uuid::parse_str(&user.id).unwrap(),
username: user.username,
email: "user@example.com".to_string(),
role: user.role,
})
}
#[get("/admin")]
fn admin_panel(_admin: crate::guards::AdminUser) -> &'static str {
"Admin Panel"
}
主程序 #
rust
#[macro_use] extern crate rocket;
mod auth;
mod guards;
mod models;
mod routes;
use auth::jwt::JwtConfig;
use std::collections::HashMap;
use std::sync::Mutex;
struct UserStore {
users: Mutex<HashMap<String, StoredUser>>,
}
struct StoredUser {
id: uuid::Uuid,
username: String,
email: String,
password_hash: String,
role: String,
}
#[launch]
fn rocket() -> _ {
rocket::build()
.manage(JwtConfig::new("secret-key".to_string(), 24))
.manage(UserStore {
users: Mutex::new(HashMap::new()),
})
.mount("/api/auth", routes![
routes::auth::register,
routes::auth::login,
routes::auth::profile,
routes::auth::admin_panel
])
}
API测试 #
bash
# 注册
curl -X POST http://localhost:8000/api/auth/register \
-H "Content-Type: application/json" \
-d '{"username":"alice","email":"alice@example.com","password":"password123"}'
# 登录
curl -X POST http://localhost:8000/api/auth/login \
-H "Content-Type: application/json" \
-d '{"username":"alice","password":"password123"}'
# 获取个人信息
curl http://localhost:8000/api/auth/profile \
-H "Authorization: Bearer {token}"
下一步 #
掌握了用户认证后,让我们继续学习 博客系统,实现一个完整的博客应用。
最后更新:2026-03-28