认证与 JWT
学习目标
- 理解 JWT 结构
- 实现登录和验证
- 掌握密码哈希
核心概念
JWT 基础
[dependencies]
jsonwebtoken = "9"
serde = { version = "1", features = ["derive"] }
chrono = "0.4"
use jsonwebtoken::{encode, decode, Header, Validation, EncodingKey, DecodingKey};
use serde::{Serialize, Deserialize};
#[derive(Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
}
fn create_token(user_id: &str) -> Result<String, jsonwebtoken::errors::Error> {
let claims = Claims {
sub: user_id.to_string(),
exp: chrono::Utc::now()
.checked_add_signed(chrono::Duration::hours(24))
.unwrap()
.timestamp() as usize,
};
encode(
&Header::default(),
&claims,
&EncodingKey::from_secret("secret".as_ref()),
)
}
fn verify_token(token: &str) -> Result<Claims, jsonwebtoken::errors::Error> {
let token_data = decode::<Claims>(
token,
&DecodingKey::from_secret("secret".as_ref()),
&Validation::default(),
)?;
Ok(token_data.claims)
}
Axum 认证中间件
use axum::{
extract::Request,
middleware::{self, Next},
response::Response,
http::StatusCode,
};
async fn auth_middleware(
mut req: Request,
next: Next,
) -> Result<Response, StatusCode> {
let token = req.headers()
.get("Authorization")
.and_then(|v| v.to_str().ok())
.and_then(|v| v.strip_prefix("Bearer "))
.ok_or(StatusCode::UNAUTHORIZED)?;
let claims = verify_token(token).map_err(|_| StatusCode::UNAUTHORIZED)?;
req.extensions_mut().insert(claims);
Ok(next.run(req).await)
}
密码哈希
[dependencies]
argon2 = "0.5"
use argon2::{
password_hash::{
rand_core::OsRng, PasswordHash, PasswordHasher, PasswordVerifier, SaltString,
},
Argon2,
};
fn hash_password(password: &str) -> String {
let salt = SaltString::generate(&mut OsRng);
let argon2 = Argon2::default();
argon2.hash_password(password.as_bytes(), &salt)
.unwrap()
.to_string()
}
fn verify_password(password: &str, hash: &str) -> bool {
let parsed_hash = PasswordHash::new(hash).unwrap();
Argon2::default().verify_password(password.as_bytes(), &parsed_hash).is_ok()
}
小结
| 操作 | 库 |
|---|
| JWT 创建 | jsonwebtoken::encode |
| JWT 验证 | jsonwebtoken::decode |
| 密码哈希 | argon2 |