Cookie与Session #

Cookie和Session是Web应用中实现用户会话管理的基础。本节将介绍如何在Rocket中安全地使用Cookie和Session。

Cookie基础 #

读取Cookie #

rust
use rocket::http::CookieJar;

#[get("/")]
fn index(jar: &CookieJar<'_>) -> String {
    match jar.get("username") {
        Some(cookie) => format!("Hello, {}", cookie.value()),
        None => "Hello, guest".to_string(),
    }
}

设置Cookie #

rust
use rocket::http::{Cookie, SameSite};

#[get("/login")]
fn login(jar: &CookieJar<'_>) -> &'static str {
    jar.add(
        Cookie::build(("username", "alice"))
            .http_only(true)
            .secure(true)
            .same_site(SameSite::Lax)
            .path("/")
            .finish()
    );
    "Logged in"
}

删除Cookie #

rust
#[get("/logout")]
fn logout(jar: &CookieJar<'_>) -> &'static str {
    jar.remove("username");
    "Logged out"
}

Cookie属性 #

安全属性 #

属性 说明 建议
HttpOnly 防止JavaScript访问 始终启用
Secure 仅HTTPS传输 生产环境启用
SameSite 跨站请求控制 Lax或Strict
Path Cookie作用路径 按需设置
Domain Cookie作用域名 按需设置
Max-Age 有效期(秒) 按需设置

CookieBuilder #

rust
use rocket::http::{Cookie, SameSite};

let cookie = Cookie::build(("session_id", "abc123"))
    .http_only(true)
    .secure(true)
    .same_site(SameSite::Strict)
    .path("/")
    .max_age(rocket::time::Duration::hours(24))
    .finish();

私有Cookie #

配置密钥 #

toml
[default]
secret_key = "your-256-bit-secret-key-here"

使用私有Cookie #

rust
use rocket::http::{Cookie, SameSite, cookie::CookieJar};

#[get("/private")]
fn set_private(jar: &CookieJar<'_>) -> &'static str {
    jar.add_private(
        Cookie::build(("user_id", "123"))
            .http_only(true)
            .finish()
    );
    "Private cookie set"
}

#[get("/get-private")]
fn get_private(jar: &CookieJar<'_>) -> String {
    match jar.get_private("user_id") {
        Some(cookie) => format!("User ID: {}", cookie.value()),
        None => "Not logged in".to_string(),
    }
}

Session管理 #

内存Session存储 #

rust
use std::collections::HashMap;
use std::sync::Mutex;
use rocket::State;

pub struct SessionStore {
    sessions: Mutex<HashMap<String, Session>>,
}

#[derive(Clone, serde::Serialize)]
pub struct Session {
    pub user_id: u32,
    pub username: String,
    pub created_at: std::time::Instant,
}

impl SessionStore {
    pub fn new() -> Self {
        Self {
            sessions: Mutex::new(HashMap::new()),
        }
    }
    
    pub fn create(&self, user_id: u32, username: String) -> String {
        let session_id = uuid::Uuid::new_v4().to_string();
        let session = Session {
            user_id,
            username,
            created_at: std::time::Instant::now(),
        };
        
        self.sessions.lock().unwrap().insert(session_id.clone(), session);
        session_id
    }
    
    pub fn get(&self, session_id: &str) -> Option<Session> {
        self.sessions.lock().unwrap().get(session_id).cloned()
    }
    
    pub fn remove(&self, session_id: &str) {
        self.sessions.lock().unwrap().remove(session_id);
    }
}

Session中间件 #

rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;

pub struct CurrentUser {
    pub user_id: u32,
    pub username: String,
}

#[rocket::async_trait]
impl<'r> FromRequest<'r> for CurrentUser {
    type Error = ();

    async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
        let jar = request.cookies();
        let store = request.guard::<&State<SessionStore>>().await.unwrap();
        
        match jar.get_private("session_id") {
            Some(cookie) => {
                match store.get(cookie.value()) {
                    Some(session) => Outcome::Success(CurrentUser {
                        user_id: session.user_id,
                        username: session.username,
                    }),
                    None => Outcome::Error((Status::Unauthorized, ())),
                }
            }
            None => Outcome::Error((Status::Unauthorized, ())),
        }
    }
}

使用Session #

rust
#[get("/login")]
fn login(store: &State<SessionStore>, jar: &CookieJar<'_>) -> &'static str {
    let session_id = store.create(1, "alice".to_string());
    jar.add_private(Cookie::new("session_id", session_id));
    "Logged in"
}

#[get("/profile")]
fn profile(user: CurrentUser) -> String {
    format!("Profile of {} (ID: {})", user.username, user.user_id)
}

#[get("/logout")]
fn logout(store: &State<SessionStore>, jar: &CookieJar<'_>) -> &'static str {
    if let Some(cookie) = jar.get_private("session_id") {
        store.remove(cookie.value());
    }
    jar.remove_private("session_id");
    "Logged out"
}

完整示例 #

rust
#[macro_use] extern crate rocket;

use rocket::http::{Cookie, CookieJar, SameSite};
use rocket::State;
use std::collections::HashMap;
use std::sync::Mutex;

struct SessionStore {
    sessions: Mutex<HashMap<String, Session>>,
}

#[derive(Clone)]
struct Session {
    user_id: u32,
    username: String,
}

impl SessionStore {
    fn new() -> Self {
        Self {
            sessions: Mutex::new(HashMap::new()),
        }
    }
    
    fn create(&self, user_id: u32, username: String) -> String {
        let id = uuid::Uuid::new_v4().to_string();
        self.sessions.lock().unwrap().insert(id.clone(), Session { user_id, username });
        id
    }
    
    fn get(&self, id: &str) -> Option<Session> {
        self.sessions.lock().unwrap().get(id).cloned()
    }
    
    fn remove(&self, id: &str) {
        self.sessions.lock().unwrap().remove(id);
    }
}

#[get("/")]
fn index(jar: &CookieJar<'_>) -> String {
    match jar.get("username") {
        Some(c) => format!("Welcome back, {}", c.value()),
        None => "Welcome, guest".to_string(),
    }
}

#[get("/login/<name>")]
fn login(name: &str, jar: &CookieJar<'_>, store: &State<SessionStore>) -> String {
    let session_id = store.create(1, name.to_string());
    
    jar.add_private(Cookie::new("session_id", session_id));
    jar.add(
        Cookie::build(("username", name))
            .http_only(true)
            .same_site(SameSite::Lax)
            .finish()
    );
    
    format!("Logged in as {}", name)
}

#[get("/logout")]
fn logout(jar: &CookieJar<'_>, store: &State<SessionStore>) -> &'static str {
    if let Some(cookie) = jar.get_private("session_id") {
        store.remove(cookie.value());
    }
    jar.remove_private("session_id");
    jar.remove("username");
    "Logged out"
}

#[launch]
fn rocket() -> _ {
    rocket::build()
        .manage(SessionStore::new())
        .mount("/", routes![index, login, logout])
}

下一步 #

掌握了Cookie和Session后,让我们继续学习 身份验证,了解如何实现完整的认证系统。

最后更新:2026-03-28