add first commit

This commit is contained in:
2026-07-21 15:44:55 +08:00
commit 1e2fa9e3bb
6 changed files with 1377 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
// utils/jwt/src/lib.rs
use chrono::{Duration, Utc};
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use serde::{Deserialize, Serialize};
use std::collections::{HashMap, HashSet};
// ---- Claims ----
#[derive(Debug, Serialize, Deserialize)]
struct Claims {
sub: String,
exp: usize,
iat: usize,
#[serde(flatten)]
extra: HashMap<String, serde_json::Value>,
}
// ---- JWT 类 ----
#[pyclass]
struct JWT {
secret: Vec<u8>,
algorithm: jsonwebtoken::Algorithm,
}
#[pymethods]
impl JWT {
#[new]
fn new(secret: String, algorithm: Option<String>) -> PyResult<Self> {
let alg = match algorithm.unwrap_or_else(|| "HS256".to_string()).as_str() {
"HS256" => jsonwebtoken::Algorithm::HS256,
"HS384" => jsonwebtoken::Algorithm::HS384,
"HS512" => jsonwebtoken::Algorithm::HS512,
"RS256" => jsonwebtoken::Algorithm::RS256,
"RS384" => jsonwebtoken::Algorithm::RS384,
"RS512" => jsonwebtoken::Algorithm::RS512,
_ => return Err(PyValueError::new_err("Unsupported algorithm")),
};
Ok(JWT {
secret: secret.into_bytes(),
algorithm: alg,
})
}
fn encode(&self, payload: String, expires_in_seconds: Option<u64>) -> PyResult<String> {
let mut claims_map: HashMap<String, serde_json::Value> = serde_json::from_str(&payload)
.map_err(|e| PyValueError::new_err(format!("Invalid JSON payload: {}", e)))?;
let sub = claims_map
.remove("sub")
.and_then(|v| v.as_str().map(String::from))
.ok_or_else(|| PyValueError::new_err("Missing 'sub' field in payload"))?;
let now = Utc::now();
let exp = now + Duration::seconds(expires_in_seconds.unwrap_or(3600) as i64);
let claims = Claims {
sub,
iat: now.timestamp() as usize,
exp: exp.timestamp() as usize,
extra: claims_map,
};
let token = jsonwebtoken::encode(
&jsonwebtoken::Header::new(self.algorithm),
&claims,
&jsonwebtoken::EncodingKey::from_secret(&self.secret),
)
.map_err(|e| PyValueError::new_err(format!("Encode failed: {}", e)))?;
Ok(token)
}
fn decode(&self, token: String) -> PyResult<String> {
let mut validation = jsonwebtoken::Validation::new(self.algorithm);
validation.validate_exp = true;
let mut required = HashSet::new();
required.insert("exp".to_string());
required.insert("iat".to_string());
validation.required_spec_claims = required;
let key = jsonwebtoken::DecodingKey::from_secret(&self.secret);
let data = jsonwebtoken::decode::<serde_json::Value>(&token, &key, &validation)
.map_err(|e| PyValueError::new_err(format!("Invalid token: {}", e)))?;
Ok(serde_json::to_string(&data.claims)
.map_err(|e| PyValueError::new_err(format!("Serialize failed: {}", e)))?)
}
fn validate(&self, token: String) -> PyResult<bool> {
let mut validation = jsonwebtoken::Validation::new(self.algorithm);
validation.validate_exp = true;
let mut required = HashSet::new();
required.insert("exp".to_string());
required.insert("iat".to_string());
validation.required_spec_claims = required;
let key = jsonwebtoken::DecodingKey::from_secret(&self.secret);
match jsonwebtoken::decode::<serde_json::Value>(&token, &key, &validation) {
Ok(_) => Ok(true),
Err(_) => Ok(false),
}
}
}
// ---- PyO3 模块 ----
#[pymodule]
fn chongming_jwt(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_class::<JWT>()?;
Ok(())
}