这是一个使用actix-web的Middleware方式实现的认证与授权框架。
chimes-auth将这个过程分为两个部分,一个是Middleware部分,实现对service的拦截处理;另一个部分是一个RBAC的抽象模型。
chimes-auth的中间件名称为ChimesAuthorization,可以使用该结构来创建chimes-auth的中间件。使用方式为:
ChimesAuthorization::new(auth_service)
然后在Actix-Web的App中进行wrap注册。
App::new()
.wrap(ChimesAuthorization::new(cuas.clone())
.header_key(&"Authentication".to_string())
.allow(&"/api/v1/login".to_string())
.allow(&"/api/v1/info".to_string())
)
ChimesAuthorization提供了headerkey,以及sessionkey(使用session feature时)两个可变参数,这两个参数表示从哪里获取Token;allow可以设置直接ByPass的URL,当访问到这些URL时将不会进行验证,而是直接执行后续的服务。
当使用headerkey是,ChimesAuthorization中间件会从HTTP请求头中取得对应的值作为Authorization Token,并将其交由ChimesAuthService来验证,其将验证该Token是否为一个有效的Token,且该Token所对应的用户信息是不是有效的用户等。 如果打开了Session的Feature,则会根据sessionkey来取被保存在Session中的用户信息。
ChimesAuthorization在处理用户请求时,通常会有以下几种情况:
在actix-web中,我们将一个请求的URL叫做一个资源(ChimesResource)。所以在chimes-auth的最小的访问单元是ChimesResource。 ChimesResource { method: String, urlpattern: String, bypass: enum, // 可取值为anonymous(匿名可访问), user(登录用户可访问), permit(授权用户可访问) }
接下就是WHO的问题,在chimes-auth中我们使用ChimesAuthUser的特征(trait)来表示,其实在ChimesAuthUser中,我们非常关心的用户的名称(user_name),它必须是唯一的,我们通过它可以查询到用户信息。
最终的问题是,怎么管理用户可以访问的资源。一种方式,就是我们需要建立ChimesAuthUser与ChimesResource中的对应关系,这是一个多对多的关系。通常为了更好的管理这些关系,还会建立Role体系(角色),也就是所谓的RBAC体系了。
当然,在chimes-auth中,并不需要这么复杂,那些RBAC都是管理方面的事情,chimes-auth不需要知道用户有什么角色,哪些角色可以访问哪些资源的问题。在chimes-auth中,整个体系只是回答了一个问题:当前用户可以访问当前请求吗?而这个问题,最终也是要交由项目的开发者来回答的。 所以,在chimes-auth中,需要实现特征ChimesAuthService: 1. authenticate 从当前请求中得到用户信息; 2. permit 判断当前请求的用户是否能够访问该请求;
ChimesAuthUser特征的实现,以及ChimesAuthService特征的实现,如下: ```
pub struct SystemUser { user_name: String, password: String, }
impl ChimesAuthUser
fn get_user_name(&self) -> String {
self.user_name.clone()
}
fn get_creditial(&self) -> String {
self.password.clone()
}
fn to_detail(&self) -> &SystemUser {
self
}
}
pub struct ChimesUserAuthService
impl ChimesAuthService
type Future = Pin<Box<dyn Future<Output=Option<SystemUser>>>>;
fn permit(&self, ust: &Option<SystemUser>, req_method: &String, url_pattern: &String) -> Self::Future {
let up = url_pattern.clone();
Box::pin(async move {
if up == "/" {
return Some(SystemUser::default())
} else {
return None
}
})
}
fn authenticate(&self, token: &String) -> Self::Future {
let rb = get_rbatis();
Box::pin(async move {
match MorinkhuurUser::from_id(rb, &1i64).await {
Ok(r) => {
match r {
Some(u) => {
Some(SystemUser {
user_name: u.username.unwrap(),
password: u.api_password.unwrap(),
})
}
None => {
None
}
}
}
Err(_) => {
None
}
}
})
}
}
ChimesAuthorization在actix-web中的注册的例子:
async fn startwebserver(webconf: &WebServerConfig) -> std::io::Result<()> {
// 设置服务器运行ip和端口信息
let ip = format!("{}:{}", "0.0.0.0", webconf.port.clone());
log::info!("App is listening on {}.", ip.clone());
// 启动一个web服务
let cuas = ChimesUserAuthService { system_user: None };
HttpServer::new(move || {
App::new()
.wrap(ChimesAuthorization::new(cuas.clone())
.header_key(&"Authentication".to_string())
.allow(&"/api/v1/login".to_string())
.allow(&"/api/v1/info".to_string())
)
.service(index_handler)
.service(crate::handler::query_user_paged)
.service(crate::handler::query_user_query)
})
.bind(ip)?
.run()
.await
} ```
捐赠