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
+72
View File
@@ -0,0 +1,72 @@
/target
# Byte-compiled / optimized / DLL files
__pycache__/
.pytest_cache/
*.py[cod]
# C extensions
*.so
# Distribution / packaging
.Python
.venv/
env/
bin/
build/
develop-eggs/
dist/
eggs/
lib/
lib64/
parts/
sdist/
var/
include/
man/
venv/
*.egg-info/
.installed.cfg
*.egg
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
pip-selfcheck.json
# Unit test / coverage reports
htmlcov/
.tox/
.coverage
.cache
nosetests.xml
coverage.xml
# Translations
*.mo
# Mr Developer
.mr.developer.cfg
.project
.pydevproject
# Rope
.ropeproject
# Django stuff:
*.log
*.pot
.DS_Store
# Sphinx documentation
docs/_build/
# PyCharm
.idea/
# VSCode
.vscode/
# Pyenv
.python-version
Generated
+1156
View File
File diff suppressed because it is too large Load Diff
+16
View File
@@ -0,0 +1,16 @@
[package]
name = "chongming-jwt"
version = "0.1.0"
edition = "2024"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[lib]
name = "chongming_jwt"
crate-type = ["cdylib"]
[dependencies]
chrono = "0.4.45"
jsonwebtoken = { version = "10.4.0", features = ["rust_crypto"] }
pyo3 = "0.29.0"
serde = "1.0.229"
serde_json = "1.0.150"
+8
View File
@@ -0,0 +1,8 @@
# utils/jwt/chongming_jwt.pyi
from typing import Optional
class JWT:
def __init__(self, secret: str, algorithm: Optional[str] = None) -> None: ...
def encode(self, payload: str, expires_in_seconds: Optional[int] = None) -> str: ...
def decode(self, token: str) -> str: ...
def validate(self, token: str) -> bool: ...
+13
View File
@@ -0,0 +1,13 @@
[build-system]
requires = ["maturin>=1.14,<2.0"]
build-backend = "maturin"
[project]
name = "chongming-jwt"
requires-python = ">=3.8"
classifiers = [
"Programming Language :: Rust",
"Programming Language :: Python :: Implementation :: CPython",
"Programming Language :: Python :: Implementation :: PyPy",
]
dynamic = ["version"]
+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(())
}