请求守卫 #
请求守卫是Rocket中实现请求级别验证和权限控制的核心机制。通过实现 FromRequest trait,可以在路由处理前进行各种检查。
FromRequest Trait #
基本定义 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
#[rocket::async_trait]
impl<'r> FromRequest<'r> for MyGuard {
type Error = MyError;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
if check_condition(request) {
Outcome::Success(MyGuard)
} else {
Outcome::Error((Status::Unauthorized, MyError))
}
}
}
Outcome类型 #
| Outcome | 说明 |
|---|---|
Success(value) |
验证成功,继续处理 |
Error((status, error)) |
验证失败,返回错误 |
Forward(status) |
转发到下一个路由 |
基本认证守卫 #
API Key认证 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
pub struct ApiKey(pub String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ApiKey {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match request.headers().get_one("X-API-Key") {
Some(key) if is_valid_key(key) => {
Outcome::Success(ApiKey(key.to_string()))
}
_ => Outcome::Error((Status::Unauthorized, ())),
}
}
}
fn is_valid_key(key: &str) -> bool {
key == "secret-api-key"
}
#[get("/protected")]
fn protected(api_key: ApiKey) -> String {
format!("Access granted with key: {}", api_key.0)
}
Bearer Token认证 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
pub struct User {
pub id: u32,
pub username: String,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for User {
type Error = AuthError;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let auth_header = request.headers().get_one("Authorization");
match auth_header {
Some(header) if header.starts_with("Bearer ") => {
let token = header.trim_start_matches("Bearer ");
match validate_token(token).await {
Ok(user) => Outcome::Success(user),
Err(e) => Outcome::Error((Status::Unauthorized, e)),
}
}
_ => Outcome::Error((Status::Unauthorized, AuthError::MissingToken)),
}
}
}
#[derive(Debug)]
pub enum AuthError {
MissingToken,
InvalidToken,
}
async fn validate_token(token: &str) -> Result<User, AuthError> {
if token == "valid-token" {
Ok(User { id: 1, username: "alice".to_string() })
} else {
Err(AuthError::InvalidToken)
}
}
#[get("/profile")]
fn profile(user: User) -> String {
format!("Welcome, {}!", user.username)
}
角色权限守卫 #
角色定义 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
#[derive(Debug, Clone, PartialEq)]
pub enum Role {
Admin,
Moderator,
User,
}
pub struct AuthUser {
pub id: u32,
pub username: String,
pub role: Role,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthUser {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let token = request.headers()
.get_one("Authorization")
.and_then(|h| h.strip_prefix("Bearer "));
match token {
Some("admin-token") => Outcome::Success(AuthUser {
id: 1,
username: "admin".to_string(),
role: Role::Admin,
}),
Some("mod-token") => Outcome::Success(AuthUser {
id: 2,
username: "moderator".to_string(),
role: Role::Moderator,
}),
Some("user-token") => Outcome::Success(AuthUser {
id: 3,
username: "user".to_string(),
role: Role::User,
}),
_ => Outcome::Error((Status::Unauthorized, ())),
}
}
}
管理员守卫 #
rust
pub struct AdminUser(pub AuthUser);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let outcome = AuthUser::from_request(request).await;
match outcome {
Outcome::Success(user) if user.role == Role::Admin => {
Outcome::Success(AdminUser(user))
}
Outcome::Success(_) => Outcome::Error((Status::Forbidden, ())),
Outcome::Error(e) => Outcome::Error(e),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[get("/admin/dashboard")]
fn admin_dashboard(admin: AdminUser) -> String {
format!("Admin Dashboard - Welcome {}", admin.0.username)
}
组合守卫 #
多重验证 #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
pub struct Authenticated {
pub user_id: u32,
}
pub struct RateLimited {
pub remaining: u32,
}
#[get("/api/data")]
fn api_data(auth: Authenticated, rate: RateLimited) -> String {
format!("User {} - {} requests remaining", auth.user_id, rate.remaining)
}
可选守卫 #
使用Option #
rust
#[get("/content")]
fn content(user: Option<AuthUser>) -> String {
match user {
Some(u) => format!("Private content for {}", u.username),
None => "Public content".to_string(),
}
}
使用Result #
rust
#[get("/profile")]
fn profile(user: Result<AuthUser, AuthError>) -> Result<String, Status> {
user.map(|u| format!("Profile of {}", u.username))
.map_err(|_| Status::Unauthorized)
}
请求信息守卫 #
获取客户端IP #
rust
use rocket::request::{self, FromRequest, Request, Outcome};
use std::net::IpAddr;
pub struct ClientIp(pub IpAddr);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for ClientIp {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match request.client_ip() {
Some(ip) => Outcome::Success(ClientIp(ip)),
None => Outcome::Error((Status::BadRequest, ())),
}
}
}
#[get("/ip")]
fn get_ip(ip: ClientIp) -> String {
format!("Your IP: {}", ip.0)
}
获取User-Agent #
rust
pub struct UserAgent(pub String);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for UserAgent {
type Error = ();
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
match request.headers().get_one("User-Agent") {
Some(ua) => Outcome::Success(UserAgent(ua.to_string())),
None => Outcome::Success(UserAgent("Unknown".to_string())),
}
}
}
完整示例 #
rust
#[macro_use] extern crate rocket;
use rocket::request::{self, FromRequest, Request, Outcome};
use rocket::http::Status;
use rocket::serde::json::Json;
use rocket::serde::{Serialize, Deserialize};
#[derive(Debug, Clone, PartialEq)]
enum Role {
Admin,
User,
}
#[derive(Debug)]
enum AuthError {
MissingToken,
InvalidToken,
InsufficientPermissions,
}
struct AuthUser {
id: u32,
username: String,
role: Role,
}
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AuthUser {
type Error = AuthError;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let auth = request.headers().get_one("Authorization");
match auth {
Some(h) if h == "Bearer admin-token" => {
Outcome::Success(AuthUser {
id: 1,
username: "admin".to_string(),
role: Role::Admin,
})
}
Some(h) if h == "Bearer user-token" => {
Outcome::Success(AuthUser {
id: 2,
username: "user".to_string(),
role: Role::User,
})
}
Some(_) => Outcome::Error((Status::Unauthorized, AuthError::InvalidToken)),
None => Outcome::Error((Status::Unauthorized, AuthError::MissingToken)),
}
}
}
struct AdminUser(AuthUser);
#[rocket::async_trait]
impl<'r> FromRequest<'r> for AdminUser {
type Error = AuthError;
async fn from_request(request: &'r Request<'_>) -> Outcome<Self, Self::Error> {
let outcome = AuthUser::from_request(request).await;
match outcome {
Outcome::Success(user) if user.role == Role::Admin => {
Outcome::Success(AdminUser(user))
}
Outcome::Success(_) => {
Outcome::Error((Status::Forbidden, AuthError::InsufficientPermissions))
}
Outcome::Error(e) => Outcome::Error(e),
Outcome::Forward(f) => Outcome::Forward(f),
}
}
}
#[get("/public")]
fn public() -> &'static str {
"Public content"
}
#[get("/protected")]
fn protected(user: AuthUser) -> String {
format!("Protected content for {}", user.username)
}
#[get("/admin")]
fn admin_only(admin: AdminUser) -> String {
format!("Admin panel - Welcome {}", admin.0.username)
}
#[get("/optional")]
fn optional_auth(user: Option<AuthUser>) -> String {
match user {
Some(u) => format!("Hello, {}", u.username),
None => "Hello, guest".to_string(),
}
}
#[launch]
fn rocket() -> _ {
rocket::build()
.mount("/api", routes![public, protected, admin_only, optional_auth])
}
测试示例 #
bash
# 公开访问
curl http://127.0.0.1:8000/api/public
# 受保护访问(无认证)
curl http://127.0.0.1:8000/api/protected
# 受保护访问(用户认证)
curl -H "Authorization: Bearer user-token" http://127.0.0.1:8000/api/protected
# 管理员访问(用户认证 - 失败)
curl -H "Authorization: Bearer user-token" http://127.0.0.1:8000/api/admin
# 管理员访问(管理员认证)
curl -H "Authorization: Bearer admin-token" http://127.0.0.1:8000/api/admin
下一步 #
掌握了请求守卫后,让我们继续学习 Fairings中间件,了解Rocket的中间件系统。
最后更新:2026-03-28