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