Rust权限控制中间件actix-web-grants的使用:为Actix-Web提供基于角色和权限的访问控制功能
actix-web-grants
actix-web
的授权扩展,用于保护你的端点。
如何使用
- 声明你自己的权限提取器
最简单的方式是声明一个具有以下签名的函数(这样的Fn已经实现了对应的trait):
use actix_web::{dev::ServiceRequest, Error};
// 你可以使用自定义类型代替String
async fn extract(req: &ServiceRequest) -> Result<HashSet<String>, Error>
- 使用第1步定义的提取器将中间件添加到你的应用中
App::new()
.wrap(GrantsMiddleware::with_extractor(extract))
第1步和第2步可以用自定义中间件或与其他库的集成来替代。
- 用下面任意一种方便的方式保护你的端点:
使用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
}
这个示例展示了三种不同的保护方式:
- proc-macro方式(需要在路由函数上添加
#[protect]
属性) - Guard方式(使用
AuthorityGuard
) - 手动检查方式(在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 应用提供了灵活而强大的权限控制功能,可以轻松实现复杂的访问控制需求。