Files
asd-backend/src/auth.rs

121 lines
3.6 KiB
Rust
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use chrono::Utc;
use jsonwebtoken::{Algorithm, DecodingKey, EncodingKey, Header, Validation, decode, encode};
use crate::models::{Claims, TempTokenClaims};
use actix_web::{
Error, HttpMessage, body::MessageBody, dev::ServiceRequest, dev::ServiceResponse,
middleware::Next,
}; // 新增:用于包装中间件函数
// 中间件函数:泛型 B 约束为 MessageBody返回 Result<ServiceResponse<B>, Error>
pub async fn jwt_middleware<B: MessageBody>(
req: ServiceRequest,
next: Next<B>,
) -> Result<ServiceResponse<B>, Error> {
// (保持原有逻辑不变)
let auth_header = req
.headers()
.get("Authorization")
.ok_or_else(|| actix_web::error::ErrorUnauthorized("缺少Authorization头"))?
.to_str()
.map_err(|_| actix_web::error::ErrorUnauthorized("Authorization格式无效"))?;
let token = auth_header.strip_prefix("Bearer ").ok_or_else(|| {
actix_web::error::ErrorUnauthorized("Authorization格式应为 Bearer <token>")
})?;
let jwt_secret = std::env::var("JWT_SECRET")
.map_err(|_| actix_web::error::ErrorInternalServerError("服务器未配置JWT密钥"))?;
let claims = verify_token(token, &jwt_secret)
.map_err(|e| actix_web::error::ErrorUnauthorized(format!("无效的token: {}", e)))?;
req.extensions_mut().insert(claims);
next.call(req).await
}
// 生成JWT的函数
pub fn generate_token(
user_id: i32,
openid: &str,
user_type: i32,
secret: &str,
) -> Result<String, String> {
// 设置过期时间:当前时间 + 24小时86400秒
let now = Utc::now();
let exp = (now + chrono::Duration::hours(24)).timestamp();
let iat = now.timestamp();
// 构建Claims
let claims = Claims {
exp,
iat,
user_id,
openid: openid.to_string(),
user_type,
};
// 生成token
encode(
&Header::new(Algorithm::HS256), // 使用HS256算法
&claims,
&EncodingKey::from_secret(secret.as_bytes()), // 签名密钥
)
.map_err(|e| format!("生成JWT失败: {}", e))
}
// 验证并解析JWT的函数
pub fn verify_token(token: &str, secret: &str) -> Result<Claims, String> {
// 验证配置指定算法默认会检查exp等字段
let validation = Validation::new(Algorithm::HS256);
// 解析token
let decoded = decode::<Claims>(
token,
&DecodingKey::from_secret(secret.as_bytes()), // 与签发时相同的密钥
&validation,
)
.map_err(|e| format!("JWT验证失败: {}", e))?;
Ok(decoded.claims)
}
// 生成临时token包含openid和resource_id短期有效
pub fn generate_temp_token(
openid: &str,
resource_id: i32,
secret: &str,
expire_minutes: i64, // 有效期(分钟)
) -> Result<String, String> {
let now = Utc::now();
let exp = (now + chrono::Duration::minutes(expire_minutes)).timestamp();
let iat = now.timestamp();
let claims = TempTokenClaims {
exp,
iat,
openid: openid.to_string(),
resource_id,
};
encode(
&Header::new(Algorithm::HS256),
&claims,
&EncodingKey::from_secret(secret.as_bytes()),
)
.map_err(|e| format!("生成临时token失败: {}", e))
}
// 验证临时token并返回载荷
pub fn verify_temp_token(token: &str, secret: &str) -> Result<TempTokenClaims, String> {
let validation = Validation::new(Algorithm::HS256);
let decoded = decode::<TempTokenClaims>(
token,
&DecodingKey::from_secret(secret.as_bytes()),
&validation,
)
.map_err(|e| format!("临时token验证失败: {}", e))?;
Ok(decoded.claims)
}