Rust权限控制中间件actix-web-grants的使用:为Actix-Web提供基于角色和权限的访问控制功能

actix-web-grants

actix-web-grants logo

actix-web的授权扩展,用于保护你的端点。

如何使用

  1. 声明你自己的权限提取器

最简单的方式是声明一个具有以下签名的函数(这样的Fn已经实现了对应的trait):

use actix_web::{dev::ServiceRequest, Error};

// 你可以使用自定义类型代替String
async fn extract(req: &ServiceRequest) -> Result<HashSet<String>, Error>
  1. 使用第1步定义的提取器将中间件添加到你的应用中
App::new()
    .wrap(GrantsMiddleware::with_extractor(extract))

第1步和第2步可以用自定义中间件或与其他库的集成来替代。

  1. 用下面任意一种方便的方式保护你的端点:

使用proc-macro方式保护的示例

#[get("/secure")]
#[actix_web_grants::protect("OP_READ_SECURED_INFO")]
async fn macro_secured() -> HttpResponse {
    HttpResponse::Ok().body("ADMIN_RESPONSE")
}

ABAC-like保护和自定义权限类型的示例:

use enums::Role::{self, ADMIN};
use dto::User;

#[get("/info/{user_id}")]
#[actix_web_grants::protect("ADMIN", expr = "user_id.into_inner() == user.id", ty = "Role")]
async fn macro_secured(user_id: web::Path<i32>, user: web::Data<User>) -> HttpResponse {
    HttpResponse::Ok().body("some secured response")
}

#[post("/info/{user_id}")]
#[actix_web_grants::protect(any("ADMIN", expr = "user.is_super_user()"), ty = "Role")]
async fn admin_or_super_user(user_id: web::Path<i32>, user: web::Data<User>) -> HttpResponse {
    HttpResponse::Ok().body("some secured response")
}

使用Guard方式保护的示例

use actix_web_grants::{AuthorityGuard, GrantsMiddleware};

App::new()
    .wrap(GrantsMiddleware::with_extractor(extract))
    .service(web::resource("/admin")
            .to(|| async { HttpResponse::Ok().finish() })
            .guard(AuthorityGuard::new("ROLE_ADMIN".to_string())))
    .service(web::resource("/admin") // 如果你想返回403 HTTP代码的备用端点
            .to(|| async { HttpResponse::Forbidden().finish() }))

使用Guard为Scope自定义备用端点的示例:

use actix_web_grants::{AuthorityGuard, GrantsMiddleware};
use actix_web::http::header;

App::new()
    .wrap(GrantsMiddleware::with_extractor(extract))
    .service(web::scope("/admin")
        .guard(AuthorityGuard::new("ROLE_ADMIN_ACCESS".to_string()))
        .service(web::resource("/users")
            .to(|| async { HttpResponse::Ok().finish() }))
    ).service(
        web::resource("/admin{regex:$|/.*?}").to(|| async { 
            HttpResponse::TemporaryRedirect().append_header((header::LOCATION, "/login")).finish()
        }))

手动方式保护的示例

use actix_web_grants::authorities::{AuthDetails, AuthoritiesCheck};

async fn manual_secure(details: AuthDetails) -> HttpResponse {
    if details.has_authority(ROLE_ADMIN) {
        return HttpResponse::Ok().body("ADMIN_RESPONSE");
    }
    HttpResponse::Ok().body("OTHER_RESPONSE")
}

支持的actix-web版本

  • 对于actix-web-grants: 2.*,支持的actix-web版本是3.*
  • 对于actix-web-grants: 3.*4.*,支持的actix-web版本是4.*

完整示例代码

下面是一个完整的示例,展示如何使用actix-web-grants:

use actix_web::{web, App, HttpResponse, HttpServer};
use actix_web_grants::{GrantsMiddleware, AuthorityGuard};
use std::collections::HashSet;

// 权限提取函数
async fn extract(_req: &actix_web::dev::ServiceRequest) -> Result<HashSet<String>, actix_web::Error> {
    // 这里应该实现实际的权限提取逻辑
    // 示例中我们返回一个包含"ROLE_ADMIN"的集合
    Ok(HashSet::from(["ROLE_ADMIN".to_string()]))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(move || {
        App::new()
            // 添加权限中间件
            .wrap(GrantsMiddleware::with_extractor(extract))
            
            // 使用proc-macro方式保护的路由
            .service(
                web::resource("/macro-secured")
                    .to(|| async { HttpResponse::Ok().body("Macro secured endpoint") })
            )
            
            // 使用Guard方式保护的路由
            .service(
                web::resource("/guard-secured")
                    .to(|| async { HttpResponse::Ok().body("Guard secured endpoint") })
                    .guard(AuthorityGuard::new("ROLE_ADMIN".to_string()))
            )
            
            // 手动检查方式
            .service(
                web::resource("/manual-secured")
                    .to(|details: actix_web_grants::authorities::AuthDetails| async move {
                        if details.has_authority("ROLE_ADMIN") {
                            HttpResponse::Ok().body("Manual secured endpoint - authorized")
                        } else {
                            HttpResponse::Forbidden().body("Manual secured endpoint - forbidden")
                        }
                    })
            )
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

这个示例展示了三种不同的保护方式:

  1. proc-macro方式(需要在路由函数上添加#[protect]属性)
  2. Guard方式(使用AuthorityGuard
  3. 手动检查方式(在handler中直接检查权限)

要运行这个示例,请确保在Cargo.toml中添加了以下依赖:

[dependencies]
actix-web = "4"
actix-web-grants = "4"

这个示例提供了一个基础框架,你可以根据自己的需求扩展权限提取逻辑和路由保护策略。


1 回复

Rust权限控制中间件actix-web-grants的使用指南

actix-web-grants 是一个为 Actix-Web 框架设计的权限控制中间件,它提供了基于角色和权限的访问控制功能。

功能特点

  • 基于角色(Role-based)的访问控制
  • 基于权限(Permission-based)的访问控制
  • 与 Actix-Web 无缝集成
  • 灵活的权限检查方式

安装

Cargo.toml 中添加依赖:

[dependencies]
actix-web-grants = "3.0"
actix-web = "4.0"

基本使用方法

1. 基于角色的保护

use actix_web::{get, web, App, HttpServer, Responder};
use actix-web-grants::proc_macro::{has_roles};

#[get("/admin")]
#[has_roles("ADMIN")]
async fn admin_page() -> impl Responder {
    "Admin page"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .service(admin_page)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

2. 基于权限的保护

use actix_web::{get, web, App, HttpServer, Responder};
use actix-web-grants::proc_macro::{has_permissions};

#[get("/secured")]
#[has_permissions("OP_READ_SECURED_INFO")]
async fn secured_page() -> impl Responder {
    "Secured information"
}

完整示例

下面是一个更完整的示例,展示如何从请求中提取用户权限:

use actix_web::{get, web, App, HttpServer, Responder, Error, HttpRequest};
use actix_web_grants::{GrantsMiddleware, proc_macro::{has_roles, has_permissions}};
use std::collections::HashSet;

// 自定义权限提取函数
async fn extract_roles(req: &HttpRequest) -> Result<HashSet<String>, Error> {
    // 这里通常从请求头、JWT令牌或其他地方获取用户角色
    // 示例中硬编码了一些角色
    let roles = HashSet::from(["USER".to_owned(), "ADMIN".to_owned()]);
    Ok(roles)
}

#[get("/admin")]
#[has_roles("ADMIN")]
async fn admin_page() -> impl Responder {
    "Admin dashboard"
}

#[get("/profile")]
#[has_roles("USER", "ADMIN")]
async fn user_profile() -> impl Responder {
    "User profile"
}

#[get("/settings")]
#[has_permissions("EDIT_SETTINGS")]
async fn settings_page() -> impl Responder {
    "Settings page"
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            // 添加权限中间件
            .wrap(GrantsMiddleware::with_extractor(extract_roles))
            .service(admin_page)
            .service(user_profile)
            .service(settings_page)
    })
    .bind("127.0.0.1:8080")?
    .run()
    .await
}

高级用法

自定义权限检查

use actix_web_grants::permissions::AuthDetails;

#[get("/custom-check")]
async fn custom_check(details: AuthDetails) -> impl Responder {
    if details.has_role("ADMIN") || details.has_permission("SPECIAL_ACCESS") {
        return "Access granted";
    }
    "Access denied"
}

组合使用角色和权限

#[get("/combined")]
#[has_roles("ADMIN")]
#[has_permissions("MANAGE_USERS")]
async fn combined_check() -> impl Responder {
    "You have both ADMIN role and MANAGE_USERS permission"
}

错误处理

默认情况下,权限检查失败会返回 403 Forbidden 响应。你可以自定义错误处理:

use actix_web::{error, HttpResponse};
use actix_web_grants::permissions::AuthDetails;

async fn custom_forbidden(details: AuthDetails) -> HttpResponse {
    HttpResponse::Forbidden()
        .json(format!("Access denied. Your roles: {:?}", details.roles))
}

#[actix_web::main]
async fn main() -> std::io::Result<()> {
    HttpServer::new(|| {
        App::new()
            .wrap(
                GrantsMiddleware::with_extractor(extract_roles)
                    .error_handler(custom_forbidden)
            )
            // ... 其他服务
    })
    // ... 启动服务器
}

性能考虑

  • 权限检查是在请求处理前进行的
  • 权限提取函数应该尽量高效
  • 考虑缓存用户权限信息

actix-web-grants 为 Actix-Web 应用提供了灵活而强大的权限控制功能,可以轻松实现复杂的访问控制需求。

回到顶部