rustzen-admin 系列(第二篇):权限架构设计 - 基于 Axum 的声明式权限系统完整实现


本文介绍了一个基于 Rust Axum 框架的现代化权限系统设计与实现,重点展示了如何通过声明式 API、智能缓存和中间件架构构建高性能、可维护的权限控制系统。


一、引言:为什么要设计一个统一权限系统?

在现代 Web 应用开发中,权限控制是一个不可避免的核心需求。随着业务复杂度的增长,我们面临着以下挑战:

🔍 传统权限控制的痛点

分散式权限判断:每个 API 端点都需要手动编写权限检查代码

// ❌ 传统做法:每个 handler 都要重复权限检查
async fn user_list_handler(current_user: CurrentUser) -> Result<Json<Vec<User>>, AppError> {
    if !current_user.has_permission("system:user:list") {
        return Err(AppError::PermissionDenied);
    }
    // 业务逻辑...
}

维护困难:权限逻辑散落在各处,难以统一管理和调试

性能问题:每次请求都要查询数据库获取权限信息

安全风险:容易遗漏权限检查,或者权限判断逻辑不一致

🎯 我们的解决方案

设计一个集中、声明式、高性能的权限系统,实现:


二、设计目标与原则

🎯 权限系统核心目标

  1. 集中式声明权限:权限绑定在路由注册阶段完成,一目了然
  2. 中间件统一校验:所有权限校验逻辑集中在中间件层
  3. 自动注入用户信息:无需手动处理用户身份识别
  4. 缓存优化:避免重复查询,支持智能刷新

✅ 设计原则

最小耦合:认证(Authentication)与授权(Authorization)完全分离

// 认证中间件:只负责验证身份
pub async fn auth_middleware(/* ... */) -> Result<Response, AppError>

// 权限中间件:只负责检查权限
async fn permission_middleware(/* ... */) -> Result<Response, AppError>

可扩展性:支持单一权限、任意权限、全部权限等多种组合模式

高性能:内存缓存 + 过期刷新,最小化数据库访问

简洁可读:开发者友好的声明式 API


三、权限系统架构总览

🔄 完整请求处理流程

graph TD
    A[HTTP 请求] --> B[Auth 中间件]
    B --> C{JWT 验证}
    C -->|失败| D[401 Unauthorized]
    C -->|成功| E[注入 CurrentUser]
    E --> F[Permission 中间件]
    F --> G{权限缓存检查}
    G -->|缓存命中| H[权限校验]
    G -->|缓存失效| I[数据库查询]
    I --> J[更新缓存]
    J --> H
    H -->|权限不足| K[403 Forbidden]
    H -->|权限通过| L[执行业务 Handler]

🏗️ 模块架构设计

模块文件职责
JWT 认证core/jwt.rsToken 生成、验证、Claims 解析
用户提取auth/extractor.rsCurrentUser 结构定义与提取逻辑
认证中间件auth/middleware.rsJWT 验证、用户信息注入
权限缓存auth/permission.rs权限缓存管理、权限校验逻辑
路由扩展common/router_ext.rs声明式权限绑定 API

四、核心模块详解

4.1 JWT 身份认证模块

设计理念:JWT 只负责身份识别,不承载权限信息

#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
    pub user_id: i64,      // 用户唯一标识
    pub username: String,  // 用户名
    pub exp: usize,        // 过期时间
    pub iat: usize,        // 签发时间
}

关键特性

pub fn verify_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
    let validation = Validation::new(Algorithm::HS256);
    let token_data = decode::<Claims>(
        token,
        &DecodingKey::from_secret(JWT_CONFIG.secret.as_bytes()),
        &validation,
    )?;
    Ok(token_data.claims)
}

4.2 用户信息提取器 CurrentUser

设计亮点:实现 Axum 的 FromRequestParts,支持依赖注入

📋 CurrentUser vs Claims 语义区别

概念用途生命周期包含信息
ClaimsJWT Token 的载荷数据Token 有效期内基础身份信息
CurrentUser业务层的用户抽象单次请求内经过验证的用户信息
// Claims: JWT 解析后的原始数据,仅用于身份验证
#[derive(Debug, Serialize, Deserialize, Clone)]
pub struct Claims {
    pub user_id: i64,
    pub username: String,
    pub exp: usize,
    pub iat: usize,
}

// CurrentUser: 业务层统一的用户结构体,用于依赖注入
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct CurrentUser {
    pub user_id: i64,
    pub username: String,
}

// 🎯 关键:实现 FromRequestParts,支持自动提取
impl<S> FromRequestParts<S> for CurrentUser
where S: Send + Sync,
{
    type Rejection = AppError;

    fn from_request_parts(/* ... */) -> impl Future<Output = Result<Self, Self::Rejection>> {
        // 从 request extensions 中获取用户信息
        // 这里的数据来源于认证中间件解析 Claims 后注入的信息
    }
}

设计原则

使用方式

// ✅ 提取当前用户信息
async fn user_profile_handler(current_user: CurrentUser) -> Json<UserProfile> {
    // current_user 自动注入,包含 user_id 和 username
}

// 🔧 统一使用 CurrentUser 提取用户信息
async fn get_user_info_handler(
    current_user: CurrentUser,  // 自动注入当前用户信息
    State(pool): State<PgPool>,
) -> AppResult<Json<ApiResponse<UserInfoResponse>>> {
    let user_info = AuthService::get_user_info(&pool, current_user.user_id, &current_user.username).await?;
    Ok(ApiResponse::success(user_info))
}

4.3 智能权限缓存机制

核心设计:内存缓存 + 过期刷新 + 线程安全

/// 权限缓存条目,带过期时间
#[derive(Debug, Clone)]
pub struct UserPermissionCache {
    pub permissions: HashSet<String>,  // 用户权限集合
    pub cached_at: DateTime<Utc>,      // 缓存时间
}

/// 全局缓存管理器,线程安全
pub struct PermissionCacheManager {
    cache: Arc<RwLock<HashMap<i64, UserPermissionCache>>>,
}

智能刷新策略

pub async fn get_cached_permissions(
    pool: &PgPool,
    user_id: i64,
) -> Result<Option<UserPermissionCache>, ServiceError> {
    if let Some(cache) = PERMISSION_CACHE.get(user_id) {
        if cache.is_expired() {
            // 🔄 缓存过期,自动从数据库刷新
            let new_cache = Self::load_user_permissions_from_db(pool, user_id).await?;
            return Ok(Some(new_cache));
        }
        return Ok(Some(cache));
    }
    Ok(None)
}

性能优化


五、声明式路由权限绑定设计

5.1 RouterExt Trait 设计

核心创新:扩展 Axum Router,支持权限声明

pub trait RouterExt<S> {
    fn route_with_permission(
        self,
        path: &str,
        method_router: MethodRouter<S>,
        permissions_check: PermissionsCheck,  // 🎯 核心:权限检查配置
    ) -> Self;
}

实现原理

impl RouterExt<PgPool> for Router<PgPool> {
    fn route_with_permission(self, path: &str, method_router: MethodRouter<PgPool>, permissions_check: PermissionsCheck) -> Self {
        self.route(
            path,
            method_router.layer(axum::middleware::from_fn(move |req: Request, next: Next| {
                let permissions_check = permissions_check.clone();
                async move { permission_middleware(req, next, permissions_check).await }
            })),
        )
    }
}

5.2 灵活的权限检查模式

PermissionsCheck 枚举:支持多种权限组合逻辑

#[derive(Debug, Clone)]
pub enum PermissionsCheck {
    Single(&'static str),           // 单一权限
    Any(Vec<&'static str>),        // 任意一个权限(OR 逻辑)
    All(Vec<&'static str>),        // 全部权限(AND 逻辑)
}

使用示例

// 🔹 单一权限检查
router.route_with_permission(
    "/system/users",
    get(user_list_handler),
    PermissionsCheck::Single("system:user:list")
)

// 🔹 任意权限检查(管理员或用户管理员都可以)
router.route_with_permission(
    "/system/users",
    post(create_user_handler),
    PermissionsCheck::Any(vec!["admin:all", "system:user:create"])
)

// 🔹 全部权限检查(需要删除权限且需要确认权限)
router.route_with_permission(
    "/system/users/{id}",
    delete(delete_user_handler),
    PermissionsCheck::All(vec!["system:user:delete", "system:confirm"])
)

5.3 权限中间件核心逻辑

统一权限校验流程

async fn permission_middleware(
    request: Request,
    next: Next,
    permissions_check: PermissionsCheck,
) -> Result<Response, AppError> {
    // 1️⃣ 获取当前用户(由 auth 中间件注入)
    let current_user = request.extensions().get::<CurrentUser>()
        .ok_or(AppError::from(ServiceError::InvalidCredentials))?;

    // 2️⃣ 获取数据库连接池
    let pool = request.extensions().get::<PgPool>()
        .ok_or(AppError::from(ServiceError::DatabaseQueryFailed))?;

    // 3️⃣ 检查权限(缓存优先)
    let has_permission = PermissionService::check_permissions(
        &pool,
        current_user.user_id,
        &permissions_check
    ).await?;

    // 4️⃣ 权限不足则拒绝访问
    if !has_permission {
        return Err(AppError::from(ServiceError::PermissionDenied));
    }

    // 5️⃣ 权限通过,继续执行
    Ok(next.run(request).await)
}

六、权限缓存与 JWT 协作机制

6.1 双层缓存设计

缓存类型存储位置过期时间刷新机制作用
JWT Token客户端1-2 小时到期重新登录身份认证
权限缓存服务端内存1 小时自动刷新权限校验

6.2 缓存协作流程

登录时缓存权限

// 登录时触发权限缓存
pub async fn login(pool: &PgPool, request: LoginRequest) -> Result<LoginResponse, ServiceError> {
    let user = Self::verify_login(pool, &request.username, &request.password).await?;
    let token = jwt::generate_token(user.id, &user.username)?;

    // 获取用户信息时会自动缓存权限
    let user_info = Self::get_user_info(pool, user.id, &user.username).await?;
    Ok(LoginResponse { token, user_info })
}

// 🎯 权限缓存的关键位置
pub async fn get_user_info(pool: &PgPool, user_id: i64, username: &str) -> Result<UserInfoResponse, ServiceError> {
    // 查询用户权限
    let permissions = UserRepository::get_user_permissions(pool, user_id).await?;

    // 🎯 缓存权限到内存(1小时有效期)
    PermissionService::cache_user_permissions(user_id, permissions);

    // 返回用户信息...
}

权限检查时的缓存策略

pub async fn check_permissions(
    pool: &PgPool,
    user_id: i64,
    permissions_check: &PermissionsCheck,
) -> Result<bool, ServiceError> {
    // 🔍 尝试从缓存获取权限
    if let Some(cache) = Self::get_cached_permissions(pool, user_id).await? {
        return Ok(permissions_check.check(&cache.permissions));
    }

    // 🚫 缓存未命中,要求重新认证
    // 设计理由:正常业务流程中不会出现此情况,
    // 如果出现说明系统异常,出于安全考虑应拒绝访问
    // 支持用户禁止、删除之后退出登录
    Err(ServiceError::InvalidCredentials)
}

6.3 缓存失效与安全性

主动清除缓存的关键场景

// 1️⃣ 登出时清除权限缓存
async fn logout_handler(current_user: CurrentUser) -> AppResult<Json<ApiResponse<()>>> {
    PermissionService::clear_user_cache(current_user.user_id);
    Ok(ApiResponse::success(()))
}

// 2️⃣ 删除用户时清除缓存
pub async fn delete_user(pool: &PgPool, id: i64) -> Result<(), ServiceError> {
    let deleted = UserRepository::soft_delete(pool, id).await?;
    if !deleted {
        return Err(ServiceError::NotFound("User not found".to_string()));
    }

    // 🎯 关键:删除用户时清除其权限缓存
    PermissionService::clear_user_cache(id);
    tracing::info!("Deleted user {} and cleared associated cache", id);

    Ok(())
}

// 3️⃣ 用户角色变更时清除缓存
pub async fn update_user(/* ... */) -> Result<UserResponse, ServiceError> {
    // 更新用户角色时
    if let Some(role_ids) = request.role_ids {
        UserRepository::set_user_roles(pool, updated_user.id, &role_ids).await?;

        // 🎯 角色变更后清除权限缓存,强制重新加载
        PermissionService::clear_user_cache(updated_user.id);
        tracing::info!("Updated user {} roles and cleared permission cache", updated_user.id);
    }

    // 用户状态变更时(特别是禁用用户)
    if let Some(new_status) = request.status {
        if new_status != user.status {
            PermissionService::clear_user_cache(updated_user.id);
            tracing::info!("Updated user {} status and cleared permission cache", updated_user.id);
        }
    }
}

缓存安全策略


七、实际使用示例

7.1 系统管理模块路由配置

pub fn system_routes() -> Router<PgPool> {
    Router::new()
        // 用户管理
        .route_with_permission(
            "/users",
            get(user_list_handler),
            PermissionsCheck::Single("system:user:list")
        )
        .route_with_permission(
            "/users",
            post(create_user_handler),
            PermissionsCheck::Single("system:user:create")
        )
        .route_with_permission(
            "/users/{id}",
            put(update_user_handler),
            PermissionsCheck::Single("system:user:update")
        )
        .route_with_permission(
            "/users/{id}",
            delete(delete_user_handler),
            PermissionsCheck::All(vec!["system:user:delete", "system:confirm"])
        )

        // 角色管理
        .route_with_permission(
            "/roles",
            get(role_list_handler),
            PermissionsCheck::Any(vec!["system:role:list", "admin:all"])
        )
}

7.2 Handler 实现

// ✅ Handler 专注业务逻辑,无需关心权限检查
async fn user_list_handler(
    current_user: CurrentUser,  // 自动注入当前用户
    State(pool): State<PgPool>,
) -> AppResult<Json<ApiResponse<Vec<UserResponse>>>> {
    // 权限已在中间件层检查完毕,这里直接处理业务逻辑
    let users = UserService::list_users(&pool).await?;
    Ok(ApiResponse::success(users))
}

async fn delete_user_handler(
    current_user: CurrentUser,
    State(pool): State<PgPool>,
    Path(user_id): Path<i64>,
) -> AppResult<Json<ApiResponse<()>>> {
    // 权限检查:需要 system:user:delete AND system:confirm
    // 已在路由层声明,中间件自动处理
    UserService::delete_user(&pool, user_id).await?;
    Ok(ApiResponse::success(()))
}

八、总结

🎯 核心设计带来的多重收益

声明式权限绑定设计

智能缓存机制

中间件统一处理

📊 整体效果

设计特性开发效率性能优化安全保障可维护性扩展性
声明式 API-
智能缓存--
中间件统一--
模块化设计--

📌 查看项目入口与完整集成示例main.rs on GitHub