Rust Web身份验证库axum-login的使用:为Axum框架提供安全便捷的登录和会话管理功能
Rust Web身份验证库axum-login的使用:为Axum框架提供安全便捷的登录和会话管理功能
axum-login是一个为Axum框架提供用户识别、认证和授权的tower中间件库。
主要特性
- 用户识别、认证和授权:通过
AuthSession
轻松管理认证和授权,它也是一个提取器,可以直接在Axum处理器中使用 - 支持任意用户和后端:应用实现
AuthUser
和AuthnBackend
特性,支持任何用户类型和用户管理后端 - 用户和组权限:通过
AuthzBackend
特性支持授权,允许应用定义自定义权限 - 便捷的路由保护:通过
login_required
和permission_required
宏提供中间件保护路由访问 - 可靠的会话管理:使用
tower-sessions
进行高性能且符合人体工程学的会话管理
安装
在Cargo.toml中添加依赖:
[dependencies]
axum-login = "0.18.0"
使用示例
以下是使用axum-login的完整示例demo:
use axum::{
routing::get,
Router,
response::IntoResponse,
extract::FromRef,
};
use axum_login::{
login_required,
AuthManagerLayerBuilder,
AuthUser,
AuthnBackend,
UserId,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
// 定义用户类型
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: i64,
name: String,
password: String,
}
impl AuthUser for User {
type Id = i64;
fn id(&self) -> Self::Id {
self.id
}
fn session_auth_hash(&self) -> &[u8] {
self.password.as_bytes()
}
}
// 定义认证后端
#[derive(Debug, Clone)]
struct Backend {
users: Arc<RwLock<Vec<User>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
struct Credentials {
username: String,
password: String,
}
#[derive(Debug, thiserror::Error)]
enum AuthError {
#[error("invalid credentials")]
InvalidCredentials,
}
impl AuthnBackend for Backend {
type User = User;
type Credentials = Credentials;
type Error = AuthError;
async fn authenticate(
&self,
creds: Self::Credentials,
) -> Result<Option<Self::User>, Self::Error> {
let users = self.users.read().await;
for user in users.iter() {
if user.name == creds.username && user.password == creds.password {
return Ok(Some(user.clone()));
}
}
Ok(None)
}
async fn get_user(
&self,
user_id: &UserId<Self>,
) -> Result<Option<Self::User>, Self::Error> {
let users = self.users.read().await;
Ok(users.iter().find(|u| u.id == *user_id).cloned())
}
}
// 定义应用状态
#[derive(Debug, Clone, FromRef)]
struct AppState {
backend: Backend,
}
#[tokio::main]
async fn main() {
// 初始化用户数据库
let users = Arc::new(RwLock::new(vec![
User {
id: 1,
name: "alice".to_string(),
password: "password123".to_string(),
},
]));
let backend = Backend { users };
// 构建认证管理中间件
let auth_layer = AuthManagerLayerBuilder::new(backend.clone(), || async move {
// 会话过期时间配置
Some(3600)
}).build();
let app = Router::new()
.route("/", get(handler))
.route("/protected", get(protected_handler))
.layer(auth_layer)
.with_state(AppState { backend });
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
async fn handler() -> impl IntoResponse {
"Hello, World!"
}
#[login_required]
async fn protected_handler() -> impl IntoResponse {
"Protected content!"
}
安全保证
这个crate使用#![forbid(unsafe_code)]
确保所有实现都是100%安全的Rust代码。
完整示例代码
以下是一个更完整的示例,包含登录/注销功能:
use axum::{
extract::State,
Form,
response::{IntoResponse, Redirect},
routing::{get, post},
Router,
};
use axum_login::{
auth_session::{AuthSession, LoginGuard},
AuthManagerLayerBuilder,
AuthUser,
AuthnBackend,
UserId,
};
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;
// 1. 定义用户类型
#[derive(Debug, Clone, Serialize, Deserialize)]
struct User {
id: i64,
name: String,
password: String,
email: String,
}
impl AuthUser for User {
type Id = i64;
fn id(&self) -> Self::Id {
self.id
}
fn session_auth_hash(&self) -> &[u8] {
self.password.as_bytes()
}
}
// 2. 定义认证后端
#[derive(Debug, Clone)]
struct Backend {
users: Arc<RwLock<Vec<User>>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Credentials {
pub username: String,
pub password: String,
}
#[derive(Debug, thiserror::Error)]
pub enum AuthError {
#[error("invalid credentials")]
InvalidCredentials,
#[error("user not found")]
UserNotFound,
}
#[axum::async_trait]
impl AuthnBackend for Backend {
type User = User;
type Credentials = Credentials;
type Error = AuthError;
async fn authenticate(
&self,
creds: Self::Credentials,
) -> Result<Option<Self::User>, Self::Error> {
let users = self.users.read().await;
let user = users
.iter()
.find(|user| user.name == creds.username && user.password == creds.password);
Ok(user.cloned())
}
async fn get_user(
&self,
user_id: &UserId<Self>,
) -> Result<Option<Self::User>, Self::Error> {
let users = self.users.read().await;
let user = users.iter().find(|user| user.id == *user_id);
Ok(user.cloned())
}
}
// 3. 定义应用状态
#[derive(Clone, FromRef)]
struct AppState {
backend: Backend,
}
// 4. 处理器函数
async fn login(
mut auth_session: AuthSession<Backend>,
State(state): State<AppState>,
Form(creds): Form<Credentials>,
) -> impl IntoResponse {
let user = match auth_session.authenticate(creds).await {
Ok(Some(user)) => user,
Ok(None) => return Err(AuthError::InvalidCredentials),
Err(_) => return Err(AuthError::InvalidCredentials),
};
auth_session.login(&user).await.unwrap();
Redirect::to("/dashboard").into_response()
}
async fn logout(mut auth_session: AuthSession<Backend>) -> impl IntoResponse {
auth_session.logout().await;
Redirect::to("/login").into_response()
}
async fn dashboard(LoginGuard(user): LoginGuard<User>) -> impl IntoResponse {
format!(
"欢迎回来,{}! 你的用户ID是: {}",
user.name, user.id
)
}
async fn login_page() -> impl IntoResponse {
r#"
<!doctype html>
<html>
<head>
<title>登录</title>
</head>
<body>
<h1>登录</h1>
<form action="/login" method="post">
<div>
<label>用户名:
<input type="text" name="username" placeholder="输入用户名">
</label>
</div>
<div>
<label>密码:
<input type="password" name="password" placeholder="输入密码">
</label>
</div>
<button type="submit">登录</button>
</form>
</body>
</html>
"#
}
// 5. 主函数
#[tokio::main]
async fn main() {
// 初始化用户数据库
let users = Arc::new(RwLock::new(vec![
User {
id: 1,
name: "alice".to_string(),
password: "password123".to_string(),
email: "alice@example.com".to_string(),
},
User {
id: 2,
name: "bob".to_string(),
password: "password456".to_string(),
email: "bob@example.com".to_string(),
},
]));
let backend = Backend { users };
// 构建认证管理中间件
let auth_layer = AuthManagerLayerBuilder::new(backend.clone(), || async move {
Some(3600) // 会话过期时间(秒)
}).build();
let app = Router::new()
.route("/", get(|| async { Redirect::to("/login") }))
.route("/login", get(login_page))
.route("/login", post(login))
.route("/logout", get(logout))
.route("/dashboard", get(dashboard))
.layer(auth_layer)
.with_state(AppState { backend });
println!("服务器运行在 http://localhost:3000");
let listener = tokio::net::TcpListener::bind("0.0.0.0:3000").await.unwrap();
axum::serve(listener, app).await.unwrap();
}
安全保证
这个crate使用#![forbid(unsafe_code)]
确保所有实现都是100%安全的Rust代码。
1 回复
axum-login:为Axum框架提供安全便捷的登录和会话管理功能
介绍
axum-login 是一个为 Axum Web 框架设计的身份验证库,它简化了用户登录、会话管理和权限控制等常见身份验证功能的实现。这个库建立在 tower-sessions 之上,提供了开箱即用的解决方案,同时保持了 Axum 框架的灵活性和性能特点。
主要特性:
- 基于会话的身份验证
- 用户角色和权限管理
- 与 Axum 框架无缝集成
- 类型安全的中间件
- 可扩展的设计
安装
在 Cargo.toml 中添加依赖:
[dependencies]
axum-login = "0.7"
tokio = { version = "1.0", features = ["full"] }
axum = "0.7"
serde = { version = "1.0", features = ["derive"] }
基本使用方法
1. 定义用户模型
首先定义一个用户类型并实现必要的 trait:
use axum_login::{AuthUser, AuthnBackend, UserId};
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct User {
id: i64,
name: String,
password: String,
role: String,
}
impl AuthUser for User {
type Id = i64;
fn id(&self) -> Self::Id {
self.id
}
fn session_auth_hash(&self) -> &[u8] {
self.password.as_bytes() // 实际应用中应该使用密码哈希
}
}
2. 实现认证后端
#[derive(Debug, Clone)]
pub struct Backend;
#[derive(Debug, Clone, Deserialize)]
pub struct Credentials {
pub username: String,
pub password: String,
}
#[axum::async_trait]
impl AuthnBackend for Backend {
type User = User;
type Credentials = Credentials;
type Error = std::convert::Infallible;
async fn authenticate(
&self,
creds: Self::Credentials,
) -> Result<Option<Self::User>, Self::Error> {
// 这里应该是实际的用户验证逻辑
if creds.username == "admin" && creds.password == "password" {
Ok(Some(User {
id: 1,
name: creds.username,
password: creds.password,
role: "admin".to_string(),
}))
} else {
Ok(None)
}
}
async fn get_user(
&self,
user_id: &UserId<Self>,
) -> Result<Option<Self::User>, Self::Error> {
// 这里应该是实际的用户查询逻辑
Ok(Some(User {
id: *user_id,
name: "admin".to_string(),
password: "password".to_string(),
role: "admin".to_string(),
}))
}
}
3. 设置路由和中间件
use axum::{Router, routing::get, response::IntoResponse};
use axum_login::{login_required, AuthManagerLayerBuilder};
use tower_sessions::{MemoryStore, SessionManagerLayer};
async fn protected_handler(user: User) -> impl IntoResponse {
format!("Welcome, {}!", user.name)
}
async fn login_handler() -> impl IntoResponse {
"Please login"
}
#[tokio::main]
async fn main() {
let session_store = MemoryStore::default();
let session_layer = SessionManagerLayer::new(session_store);
let backend = Backend;
let auth_layer = AuthManagerLayerBuilder::new(backend, session_layer).build();
let app = Router::new()
.route("/protected", get(protected_handler))
.route("/login", get(login_handler))
.layer(auth_layer);
axum::Server::bind(&"0.0.0.0:3000".parse().unwrap())
.serve(app.into_make_service())
.await
.unwrap();
}
4. 添加登录/登出功能
use axum::{response::Redirect, Form};
use axum_login::axum_sessions::extractors::WritableSession;
async fn login(
mut auth: axum_login::AuthSession极狐GitLab 16.9 发布,带来多项 DevOps 增强功能
极狐GitLab 16.9 版本已正式发布,带来了多项 DevOps 增强功能,包括:
1. CI/CD 改进
- 新增 "needs" 依赖关系可视化功能
- 支持在 merge request 流水线中使用 when:manual 作业
2. 安全增强
- 静态应用安全测试(SAST)支持更多语言
- 依赖项扫描支持自动修复漏洞
3. 项目管理
- 议题看板支持自定义工作流状态
- 新增时间跟踪报告功能
4. 平台改进
- 容器镜像仓库性能优化
- 支持使用外部 PostgreSQL 数据库
该版本还包含多项性能优化和 bug 修复,建议所有用户升级。企业用户可以通过极狐GitLab 的自动更新功能或手动下载安装包进行升级。
极狐GitLab 作为专为中国市场打造的 GitLab 发行版,16.9 版本在保持与国际版 GitLab 功能同步的同时,还针对国内用户需求进行了本地化优化,包括更快的访问速度和符合中国法规的数据存储选项。