//! JWT Token 创建与验证 use chrono::{Duration, Utc}; use jsonwebtoken::{decode, encode, DecodingKey, EncodingKey, Header, Validation}; use serde::{Deserialize, Serialize}; use crate::error::SaasResult; /// JWT Claims #[derive(Debug, Serialize, Deserialize)] pub struct Claims { pub sub: String, pub role: String, pub permissions: Vec, pub iat: i64, pub exp: i64, } impl Claims { pub fn new(account_id: &str, role: &str, permissions: Vec, expiration_hours: i64) -> Self { let now = Utc::now(); Self { sub: account_id.to_string(), role: role.to_string(), permissions, iat: now.timestamp(), exp: (now + Duration::hours(expiration_hours)).timestamp(), } } } /// 创建 JWT Token pub fn create_token( account_id: &str, role: &str, permissions: Vec, secret: &str, expiration_hours: i64, ) -> SaasResult { let claims = Claims::new(account_id, role, permissions, expiration_hours); let token = encode( &Header::default(), &claims, &EncodingKey::from_secret(secret.as_bytes()), )?; Ok(token) } /// 验证 JWT Token pub fn verify_token(token: &str, secret: &str) -> SaasResult { let token_data = decode::( token, &DecodingKey::from_secret(secret.as_bytes()), &Validation::default(), )?; Ok(token_data.claims) } #[cfg(test)] mod tests { use super::*; const TEST_SECRET: &str = "test-secret-key"; #[test] fn test_create_and_verify_token() { let token = create_token( "account-123", "admin", vec!["model:read".to_string()], TEST_SECRET, 24, ).unwrap(); let claims = verify_token(&token, TEST_SECRET).unwrap(); assert_eq!(claims.sub, "account-123"); assert_eq!(claims.role, "admin"); assert_eq!(claims.permissions, vec!["model:read"]); } #[test] fn test_invalid_token() { let result = verify_token("invalid.token.here", TEST_SECRET); assert!(result.is_err()); } #[test] fn test_wrong_secret() { let token = create_token("account-123", "admin", vec![], TEST_SECRET, 24).unwrap(); let result = verify_token(&token, "wrong-secret"); assert!(result.is_err()); } }