Rust + Axum 中间件:JWT 鉴权与请求上下文注入

test12026-09-110 次阅读

把鉴权从每个 handler 里抽出来

在 Axum 里,如果每一个路由处理函数都自己解析 token、校验签名、再取用户 ID,重复代码会迅速失控。更干净的做法是用中间件(middleware):在请求进入业务 handler 之前统一完成鉴权,并把解析出的用户身份写进请求扩展(extensions),后续 handler 直接用 extractor 取,不需要再碰 HTTP 头。

中间件本身就是一个异步函数

Axum 的中间件签名很直白:拿到 Request、Next,处理完调用 next.run 继续链路。下面从 Authorization 头取出 Bearer token,用 jsonwebtoken 解码,失败直接返回 401。

use axum::{
    extract::Request,
    middleware::Next,
    response::Response,
    http::{StatusCode, HeaderMap},
};
use jsonwebtoken::{decode, DecodingKey, Validation};
use serde::Deserialize;

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

pub async fn auth_middleware(
    headers: HeaderMap,
    mut req: Request,
    next: Next,
) -> Result<Response, StatusCode> {
    let token = headers
        .get("authorization")
        .and_then(|v| v.to_str().ok())
        .and_then(|v| v.strip_prefix("Bearer "))
        .ok_or(StatusCode::UNAUTHORIZED)?;

    let data = decode::<Claims>(
        token,
        &DecodingKey::from_secret(b"secret"),
        &Validation::default(),
    )
    .map_err(|_| StatusCode::UNAUTHORIZED)?;

    req.extensions_mut().insert(data.claims.sub);
    Ok(next.run(req).await)
}

业务 handler 直接取身份

解析出的 sub 已经放进 extensions,handler 用 FromRequestParts 取回来即可。注意中间件写入的类型必须和 extractor 的类型一致,否则取不到。

use axum::extract::State;
use axum::response::IntoResponse;

async fn me(State(pool): State<Db>, sub: UserId) -> impl IntoResponse {
    let id = sub.0;
    format!("hello user {id}")
}

// 用一个新类型包裹 String,避免和别的 String extractor 冲突
struct UserId(pub String);

impl axum::extract::FromRequestParts<()> for UserId {
    type Rejection = StatusCode;
    async fn from_request_parts(
        parts: &mut axum::http::request::Parts,
        _: &(),
    ) -> Result<Self, Self::Rejection> {
        parts
            .extensions
            .get::<String>()
            .map(|s| UserId(s.clone()))
            .ok_or(StatusCode::UNAUTHORIZED)
    }
}

把中间件挂到路由上

let app = Router::new()
    .route("/me", get(me))
    .route_layer(middleware::from_fn(auth_middleware));

route_layer 只保护它后面的路由,公开接口和受保护接口可以分层挂载,互不影响。中间件不持有状态、可测试、可组合,是 Axum 里处理鉴权、日志、限流这类横切逻辑的标准位置。

T

test1

文章作者

把鉴权从每个 handler 里抽出来 在 Axum 里,如果每一个路由处理函数都自己解析 token、校验签名、再取...

分类
技术
发布时间
2026-09-11
字数
约 2200 字
阅读
0 次