6次发布

新版本 0.1.3 2024年8月12日
0.1.2 2024年8月12日
0.0.31 2024年8月1日
0.0.30 2024年7月31日

#70身份验证

Download history 444/week @ 2024-07-27 133/week @ 2024-08-03 295/week @ 2024-08-10

872 每月下载

MIT 许可证

78KB
1.5K SLoC

some-auth

此crate提供API服务中常用的身份验证逻辑(用户和角色管理、仓库、带有刷新认证的JWT),开箱即用。有关此crate使用的安全方法的更详细描述,请参阅安全方法

crates.io

目前,该crate主要关注异步使用,特别是Axum

此crate可选地(启用axum-auth功能)建议身份验证中间件,可以在每个认证用户或仅对指定的角色允许访问。

目前,PostgreSQL仓库实现可用(启用pg-repository功能),用于存储用户和刷新令牌。在开发过程中,可能会将其他实现(例如MongoDB)的一些功能添加到crate中。

该crate目前正在早期开发中,未来版本可能会实施一些破坏性更改。

设置

将some-auth crate添加到您的项目中(使用下面将要描述的axum-authpg-repository可选功能)

cargo add some-auth --features axum-auth,pg-repository

要使用crate的功能,需要创建一个UserService,它使用UserServiceBuilder创建。

默认构建器

最简单的方法是使用some_auth::default_builder()方法,该方法返回具有以下属性的默认构建器

  • 默认 AuthUser (User) 实现
  • 默认凭证验证器(见CredentialValidator.default()
  • JWT的HMAC SHA-256算法

构建器需要指定AuthRepository trait的实现,使用use_repository方法。在此示例中,我们将使用PgAuthRepository(PostgreSQL仓库),它可通过pg-repository功能获得。要查看表模式,请参阅src/repository/pg_repository.sql

示例

let repository = Arc::new(PgAuthRepository::create("postgresql://postgres:postgres@localhost:5432/postgres".to_string()).await.unwrap());
let jwt_token_settings = JwtTokenSettings {
    access_tokens_secret: "supersecret".to_string(),
    access_tokens_lifetime: TimeDelta::minutes(10),
    refresh_tokens_secret: "supersecrettoo".to_string(),
    refresh_tokens_lifetime: TimeDelta::days(7)
};
let user_service = some_auth::default_builder()
    .configure_jwt(jwt_token_settings)
    .use_repository(repository)
    .build()
    .unwrap();

PgAuthRepository 包含以下实现:impl<TAuthUser: AuthUser + fmt::Debug + Send + Sync> AuthRepository<TAuthUser> for PgRepository 其中 AuthUser 在此情况下是 User

#[async_trait]
impl<TAuthUser: AuthUser + fmt::Debug + Send + Sync> AuthRepository<TAuthUser> for PgRepository {
    async fn add_user(&self, user: &TAuthUser) -> Result<i32, String> {
        let client = open_connection(&self.conn_string).await.map_err(|err| err.to_string())?;

        let created_res = client.query_one("\
            INSERT INTO users (username, pwd_hash, blocked, created_at, updated_at)
            VALUES ($1, $2, $3, $4, $5)
            RETURNING id;",
            &[
                &user.username(),
                &user.pwd_hash(),
                &user.blocked(),
                &user.created_at(),
                &user.updated_at()
            ])
            .await
            .map_err(|err| err.to_string())?;

        Ok(created_res.get("id"))
    }

    ...other methods...
}

因此,为了正确地在关系数据库(例如示例中的 postgres)中使用实现过的仓库,需要在那里创建一些表

  1. 一个用于用户的表,其中列代表 AuthUser 获取器;
  2. 一个用于角色的表,其中列代表 Role 获取器;
  3. 用户角色的多对多表;
  4. 一个用于存储刷新令牌的表。

您还可以使用 Axum 身份验证中间件来保护您的 API(仅在有 features = [ "axum-auth" ] 功能的情况下可用)

/// Controls if user is authenticated and checks their role according to [`RoleFilter`]
pub async fn auth_middleware<'a, TAuthUser: AuthUser + fmt::Debug + Send + Sync>(
    State(state): State<Arc<UserServiceState<TAuthUser>>>,
    req: Request,
    next: Next,
    role_filter: Option<RoleFilter<'a>>
) -> Result<Response, AuthError> { ... }
let user_service_state = Arc::new(UserServiceState { user_service });

let router = Router::new()
    .route("/public-route", post(public_route_handler))
    .route("/authenticated-users-route", post(authenticated_users_route_handler))
        .route_layer(middleware::from_fn_with_state(user_service_state, |state, req, next| some_auth::auth_middleware(state, req, next, None))) // None as this route is available for every authenticated user
    .route("/role-specified-route", post(role_specified_route_handler))
        .route_layer(middleware::from_fn_with_state(user_service_state, |state, req, next| some_auth::auth_middleware(state, req, next, Some(vec!["admin", "backoffice"])))) // this route is available only for authenticated users with "admin" or "backoffice" role

手动设置

要手动设置 UserSerice,需要使用

pub fn builder<TAuthUser: AuthUser + fmt::Debug + Send + Sync>() -> UserServiceBuilder<TAuthUser>

并使用以下方法设置所有所需的配置

/// Sets [`CredentialValidator`] which will be used to valudate [`AuthUser`] credentials in [`UserService`]
pub fn set_credential_validator(mut self, validator: CredentialValidator) -> Self

/// Sets jwt algorithm which will be used in [`UserService`]
pub fn set_jwt_algorithm(mut self, algorithm: Algorithm) -> Self

/// Sets jwt token settings which will be used in [`UserService`]
pub fn configure_jwt(mut self, jwt_token_settings: JwtTokenSettings) -> Self

/// Sets the repository which will be used in [`UserService`]
pub fn use_repository(mut self, repository: Arc<dyn AuthRepository<TAuthUser> + Sync + Send>) -> Self

安全方法

JWT

此crate使用 jsonwebtoken crate 进行JWT。现在,仅提供对称加密算法(不同SHA的HMAC)。不对称算法可能在将来可用。JWT秘密和生命周期可配置。对于需要刷新令牌的操作,还有一个额外的检查,以确定提供的刷新令牌是否实际(也请参阅 Storing the secrets)。

用户验证

为了提高应用程序的安全性,默认的 CredentialValidator 对于用户凭证有以下规则

  • 至少5个字符,拉丁字母和数字的组合,用户名至少有一个字母
  • 至少12个字符,拉丁大小写字母、数字和特殊符号的组合

但当然,有时它可能太强(或太弱)。在这种情况下,可以配置自己的 CredentialValidator

存储秘密

用户模型(因此,存储库中的用户)使用bcrypt哈希密码,这可以有效防止暴力攻击。

刷新令牌也存储在存储库中,以确保用户提供的刷新令牌是真实的。为了避免数据库中的“裸”刷新令牌,它们也进行了哈希处理,但使用SHA-256(在令牌需要快速哈希检查的情况下更有用,同时与密码相比,暴力破解“随机”令牌更困难)。将来可能会实现禁用或更改令牌哈希算法的功能。

依赖关系

~7–19MB
~276K SLoC