/chimes-auth

A middleware of actix-web for handling the authentication for each request

Primary LanguageRustApache License 2.0Apache-2.0

chimes-auth

这是一个使用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提供了header_key,以及session_key(使用session feature时)两个可变参数,这两个参数表示从哪里获取Token;allow可以设置直接ByPass的URL,当访问到这些URL时将不会进行验证,而是直接执行后续的服务。

当使用header_key是,ChimesAuthorization中间件会从HTTP请求头中取得对应的值作为Authorization Token,并将其交由ChimesAuthService来验证,其将验证该Token是否为一个有效的Token,且该Token所对应的用户信息是不是有效的用户等。 如果打开了Session的Feature,则会根据session_key来取被保存在Session中的用户信息。

ChimesAuthorization在处理用户请求时,通常会有以下几种情况:

  1. user为None值 此时根据req_method和url_pattern查询到该url为bypass=anonymous的模式,则返回Some(Default) 此时根据req_method和url_pattern查询到该url为bypass=user的模式,则返回None,此时应该是需要进行登录处理 此时根据req_method和url_pattern查询到该url为bypass=permit的模式,则返回None,则返回None,此时应该是需要进行登录处理
  2. user为Some值 此时根据req_method和url_pattern查询到该url为bypass=anonymous的模式,则返回true 此时根据req_method和url_pattern查询到该url为bypass=user的模式,则返回true 此时根据req_method和url_pattern查询到该url为bypass=permit的模式,则: a. 用户拥有可以访问该权限的角色:返回Some(T) b. 用户拥有可以访问该权限的资源: 返回Some(T) c. 用户不满足a和b,则返回None

chimes-auth的RBAC实践

在actix-web中,我们将一个请求的URL叫做一个资源(ChimesResource)。所以在chimes-auth的最小的访问单元是ChimesResource。 ChimesResource { method: String, url_pattern: String, by_pass: 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特征的实现,如下:

#[derive(Clone, Default, Deserialize)]
pub struct SystemUser {
    user_name: String,
    password: String,
}

impl ChimesAuthUser<SystemUser> for SystemUser
{

    fn get_user_name(&self) -> String {
        self.user_name.clone()
    }

    fn get_creditial(&self) -> String {
        self.password.clone()
    }

    fn to_detail(&self) -> &SystemUser {
        self
    }
}

#[derive(Clone)]
pub struct ChimesUserAuthService<SystemUser> {
    #[allow(unused)]
    system_user: Option<SystemUser>
}

impl ChimesAuthService<SystemUser> for ChimesUserAuthService<SystemUser> {

    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 start_web_server(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
}

联系方式/捐赠,或 rbatis-generator 点star

捐赠

enjoylost