use base64::{Engine};
use base64::engine::general_purpose::{URL_SAFE_NO_PAD};
use chrono::Local;
use json::{JsonValue, object};
use crate::pkey::{pri_key_sign, pub_key_verify};
use crate::sha256::{encrypt_hmac_byte};
#[derive(Clone, Debug)]
pub struct Jwt {
pub headers: Header,
pub payload: Payload,
}
impl Jwt {
pub fn new(alg: Alg) -> Jwt {
Self {
headers: Header {
alg,
typ: "JWT".to_string(),
},
payload: Payload::default(),
}
}
pub fn payload(&mut self, iss: &str, sub: &str, aud: &str, jti: &str) -> &mut Self {
self.payload.iss = iss.to_string();
self.payload.sub = sub.to_string();
self.payload.aud = aud.to_string();
self.payload.jti = jti.to_string();
self
}
pub fn payload_custom(&mut self, custom: JsonValue) -> &mut Self {
self.payload.custom = custom;
self
}
pub fn payload_time(&mut self, exp: i32, nbf: i32, iat: i32) -> &mut Self {
self.payload.exp = exp;
self.payload.nbf = nbf;
self.payload.iat = iat;
self
}
pub fn token(&mut self, key: &str) -> String {
let header = self.headers.sign();
let payload = self.payload.sign();
let sign_text = format!("{header}.{payload}");
let signature = match self.headers.alg {
Alg::HS256 => base64_url_encode(encrypt_hmac_byte(key, sign_text.as_bytes()).as_slice()),
Alg::RSA => pri_key_sign(key, sign_text.as_str(), "RSA"),
Alg::RS256 => pri_key_sign(key, sign_text.as_str(), "RS256"),
};
format!("{header}.{payload}.{signature}")
}
pub fn verify(jwt_token: &str, key: &str) -> Result<Self, String> {
let data: Vec<&str> = jwt_token.split(".").collect();
if data.len() != 3 {
return Err("TOKEN格式不正确".to_string());
}
let header = data[0];
let payload = data[1];
let signature = data[2];
let sign_text = format!("{header}.{payload}");
let header = Header::from(header)?;
let payload = Payload::decode(payload)?;
let signature_new = match header.alg {
Alg::HS256 => base64_url_encode(&encrypt_hmac_byte(key, sign_text.as_bytes())),
Alg::RS256 => {
if !pub_key_verify(key.to_string().clone(), sign_text.as_str(), signature.to_string(), "RS256") {
return Err("签名不正确1".to_string());
}
signature.to_string()
}
Alg::RSA => {
let sign = base64_url_decode(signature)?;
if !pub_key_verify(key.to_string().clone(), sign_text.as_str(), sign, "RSA") {
return Err("签名不正确2".to_string());
}
signature.to_string()
}
};
if signature_new != signature {
return Err("签名不一致".to_string());
}
let time = Local::now().timestamp() as i32;
if payload.nbf > time {
return Err(format!("未生效: {}", payload.nbf - time));
}
if time > payload.exp {
return Err(format!("已过期: {}", payload.exp));
}
Ok(Self {
headers: header,
payload,
})
}
pub fn info(jwt_token: &str) -> Result<Jwt, String> {
let data: Vec<&str> = jwt_token.split(".").collect();
if data.len() != 3 {
return Err("TOKEN格式不正确".to_string());
}
let header = data[0];
let payload = data[1];
let header = Header::from(header)?;
let payload = Payload::decode(payload)?;
Ok(Self {
headers: header,
payload,
})
}
}
#[derive(Clone, Debug)]
pub enum Alg {
HS256,
RSA,
RS256,
}
impl Alg {
pub fn str(&mut self) -> String {
match self {
Alg::HS256 => "HS256",
Alg::RSA => "RSA",
Alg::RS256 => "RS256"
}.to_string()
}
pub fn from(name: &str) -> Self {
match name {
"HS256" => Alg::HS256,
"RSA" => Alg::RSA,
"RS256" => Alg::RS256,
_ => Alg::HS256
}
}
}
#[derive(Clone, Debug)]
pub struct Header {
alg: Alg,
typ: String,
}
impl Header {
pub fn sign(&mut self) -> String {
let header = self.json();
base64_url_encode(header.to_string().as_bytes())
}
pub fn set_alg(&mut self, alg: Alg) {
self.alg = alg;
}
pub fn from(base64: &str) -> Result<Header, String> {
let mut header = Header::default();
let header_text = base64_url_decode(base64)?;
header.alg = match json::parse(header_text.as_str()) {
Ok(e) => {
Alg::from(e["alg"].as_str().unwrap_or(""))
}
Err(e) => {
return Err(e.to_string());
}
};
Ok(header)
}
pub fn json(&mut self) -> JsonValue {
object! {
alg:self.alg.str(),
typ:self.typ.clone()
}
}
}
impl Default for Header {
fn default() -> Self {
Self {
alg: Alg::HS256,
typ: "JWT".to_string(),
}
}
}
#[derive(Clone)]
#[derive(Debug)]
pub struct Payload {
pub iss: String,
pub iat: i32,
pub jti: String,
pub sub: String,
pub aud: String,
pub custom: JsonValue,
pub nbf: i32,
pub exp: i32,
}
impl Default for Payload {
fn default() -> Self {
Self {
iss: "".to_string(),
iat: 0,
jti: "".to_string(),
sub: "".to_string(),
aud: "".to_string(),
custom: JsonValue::Null,
nbf: 0,
exp: 0,
}
}
}
impl Payload {
pub fn sign(&mut self) -> String {
let payload = self.json();
base64_url_encode(payload.to_string().as_bytes())
}
pub fn decode(payload: &str) -> Result<Payload, String> {
let payload_text = base64_url_decode(payload)?;
match json::parse(payload_text.as_str()) {
Ok(payload) => {
Ok(Self {
iss: payload["iss"].to_string(),
iat: payload["iat"].as_i32().unwrap_or(0),
jti: payload["jti"].to_string(),
sub: payload["sub"].to_string(),
aud: payload["aud"].to_string(),
custom: payload["custom"].clone(),
nbf: payload["nbf"].as_i32().unwrap_or(0),
exp: payload["exp"].as_i32().unwrap_or(0),
})
}
Err(_) => Err("载荷错误".to_string())
}
}
pub fn json(&mut self) -> JsonValue {
object! {
iss: self.iss.clone(),
iat: self.iat,
jti: self.jti.clone(),
sub: self.sub.clone(),
aud: self.aud.clone(),
custom: self.custom.clone(),
nbf: self.nbf,
exp: self.exp
}
}
}
pub fn base64_url_decode(base64: &str) -> Result<String, String> {
unsafe {
match URL_SAFE_NO_PAD.decode(base64) {
Ok(e) => Ok(String::from_utf8_unchecked(e)),
Err(e) => Err(e.to_string())
}
}
}
pub fn base64_url_encode(data: &[u8]) -> String {
URL_SAFE_NO_PAD.encode(data)
}
#[test]
fn tests() {
let mut jwt = Jwt::new(Alg::HS256);
jwt.payload("1", "1", "1", "1");
jwt.payload_custom(object! {"name" : "John","admin" : true});
jwt.payload_time(1751572000, 1751472000, 1751472000);
let data = jwt.token("1");
println!("{data}");
let jwt_new = Jwt::verify(data.as_str(), "1");
println!("{jwt_new:#?}");
let jwt_new = Jwt::info(data.as_str());
println!("{jwt_new:#?}");
}
#[cfg(test)]
mod test {
use crate::pkey::create_pkey_pkcs8;
use crate::jwt::Alg;
use crate::jwt::Jwt;
use json::object;
#[test]
fn test_rsa() {
let mut jwt = Jwt::new(Alg::RS256);
jwt.payload("1", "1", "1", "1");
let (pub_key, pri_key) = create_pkey_pkcs8();
jwt.payload_custom(object! {"name" : "John","admin" : true});
jwt.payload_time(1751572000, 1751472000, 1751472000);
let data = jwt.token(pri_key.as_str());
println!("{data}");
let jwt_new = Jwt::verify(data.as_str(), pub_key.as_str());
println!("{jwt_new:#?}");
let jwt_new = Jwt::info(data.as_str());
println!("{jwt_new:#?}");
}
}