用户认证系统 #

项目结构 #

text
auth-project/
├── Cargo.toml
├── src/
│   ├── main.rs
│   ├── auth.rs
│   ├── handlers.rs
│   ├── models.rs
│   └── repository.rs
└── tests/

完整实现 #

rust
use actix_web::{web, App, HttpResponse, HttpServer, Responder, Error};
use bcrypt::{hash, verify, DEFAULT_COST};
use jsonwebtoken::{decode, encode, Algorithm, DecodingKey, EncodingKey, Header, Validation};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
use std::sync::{Arc, Mutex};

#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
    id: u32,
    username: String,
    password_hash: String,
}

#[derive(Deserialize)]
struct RegisterRequest {
    username: String,
    password: String,
}

#[derive(Deserialize)]
struct LoginRequest {
    username: String,
    password: String,
}

#[derive(Serialize)]
struct LoginResponse {
    token: String,
}

#[derive(Debug, Serialize, Deserialize)]
struct Claims {
    sub: String,
    exp: usize,
}

struct AppState {
    users: Mutex<HashMap<String, User>>,
    jwt_secret: String,
}

impl AppState {
    fn new(jwt_secret: String) -> Self {
        Self {
            users: Mutex::new(HashMap::new()),
            jwt_secret,
        }
    }
    
    fn generate_token(&self, user_id: &str) -> Result<String, Error> {
        let exp = chrono::Utc::now() + chrono::Duration::hours(24);
        let claims = Claims {
            sub: user_id.to_string(),
            exp: exp.timestamp() as usize,
        };
        
        encode(
            &Header::default(),
            &claims,
            &EncodingKey::from_secret(self.jwt_secret.as_bytes()),
        )
        .map_err(|e| actix_web::error::ErrorInternalServerError(e))
    }
}

#[actix_web::post("/register")]
async fn register(
    body: web::Json<RegisterRequest>,
    state: web::Data<Arc<AppState>>,
) -> impl Responder {
    let password_hash = hash(&body.password, DEFAULT_COST).unwrap();
    
    let user = User {
        id: 1,
        username: body.username.clone(),
        password_hash,
    };
    
    state.users.lock().unwrap().insert(body.username.clone(), user);
    
    HttpResponse::Created().json(serde_json::json!({ "message": "User created" }))
}

#[actix_web::post("/login")]
async fn login(
    body: web::Json<LoginRequest>,
    state: web::Data<Arc<AppState>>,
) -> impl Responder {
    let users = state.users.lock().unwrap();
    
    match users.get(&body.username) {
        Some(user) => {
            if verify(&body.password, &user.password_hash).unwrap_or(false) {
                match state.generate_token(&user.id.to_string()) {
                    Ok(token) => HttpResponse::Ok().json(LoginResponse { token }),
                    Err(e) => HttpResponse::InternalServerError().json(serde_json::json!({ "error": e.to_string() })),
                }
            } else {
                HttpResponse::Unauthorized().json(serde_json::json!({ "error": "Invalid credentials" }))
            }
        }
        None => HttpResponse::Unauthorized().json(serde_json::json!({ "error": "User not found" })),
    }
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    let state = Arc::new(AppState::new("secret_key".to_string()));
    
    HttpServer::new(move || {
        App::new()
            .app_data(web::Data::new(state.clone()))
            .service(register)
            .service(login)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

下一步 #

继续学习 博客系统,了解完整项目开发!

最后更新:2026-03-29