use chrono::Local;
use json::{JsonValue, object};
use crypto::hmac::Hmac;
use crypto::mac::Mac;
use crypto::sha2::Sha256;
use log::info;
use crate::base64::{decode, url_decode, url_encode, url_encode_u8};
pub struct Jwt {
header: JsonValue,
payload: JsonValue,
}
impl Jwt {
pub fn default(iss: &str) -> Self {
Self {
header: object! {
"alg":"HS256",
"typ": "JWT"
},
payload: object! {
"iss":iss.clone(),
"iat":Local::now().timestamp_millis(),
"jti":uuid::Uuid::new_v4().to_string()
},
}
}
pub fn payload(&mut self, sub: &str, aud: &str, custom: JsonValue) -> &mut Self {
self.payload["sub"] = sub.into();
self.payload["aud"] = aud.into();
self.payload["custom"] = custom.into();
self
}
pub fn nbf_exp(&mut self, nbf: &str, exp: i64) -> &mut Self {
if nbf == "" {
self.payload["nbf"] = Local::now().timestamp_millis().into();
} else {
self.payload["nbf"] = nbf.into();
}
let nbf = self.payload["nbf"].as_i64().unwrap();
let exp = 60 * exp * 1000;
self.payload["exp"] = (nbf + exp).into();
self
}
pub fn sign(&mut self, key: &str) -> String {
let header = url_encode(self.header.to_string());
let payload = url_encode(self.payload.to_string());
let signature = format!("{}.{}", header, payload);
let mut hmac = Hmac::new(Sha256::new(), key.as_bytes());
hmac.input(signature.as_bytes());
let mac = hmac.result();
let sign = url_encode_u8(mac.code());
let token = format!("{}.{}.{}", header, payload, sign);
return token;
}
pub fn verify(token: &str, key: &str) -> bool {
let binding = token.to_string();
let data: Vec<&str> = binding.split(".").collect();
let header = data[0].clone();
let payload = data[1].clone();
let signature = format!("{}.{}", header, payload);
let sign_old = data[2].clone();
let header = decode(header.to_string());
let header = json::parse(header.as_str()).unwrap();
let alg = header["alg"].as_str().unwrap();
let sign_new = {
match alg {
"HS256" => {
let mut hmac = Hmac::new(Sha256::new(), key.as_bytes());
hmac.input(signature.as_bytes());
let mac = hmac.result();
url_encode_u8(mac.code())
}
_ => {
signature
}
}
};
if sign_new != sign_old {
return false;
}
let payload_text = url_decode(payload.to_string());
match json::parse(payload_text.as_str()) {
Ok(payload) => {
let time = Local::now().timestamp_millis();
let exp = payload["exp"].as_i64().unwrap();
if time >= exp {
return false;
}
let nbf = payload["nbf"].as_i64().unwrap();
if nbf > time {
return false;
}
return true;
}
Err(e) => {
info!("{}",e);
info!("payload_old:{}",payload);
info!("payload_new:{}",payload_text);
return false;
}
}
}
pub fn info(token: &str) -> JsonValue {
let binding = token.to_string();
let data: Vec<&str> = binding.split(".").collect();
let header = data[0].clone();
let payload = data[1].clone();
let header = decode(header.to_string());
let header = {
let res = json::parse(header.as_str());
match res {
Ok(e) => e,
Err(_) => object! {}
}
};
let payload = url_decode(payload.to_string());
let payload = {
let res = json::parse(payload.as_str());
match res {
Ok(e) => e,
Err(_) => object! {}
}
};
return object! {
"header":header,
"payload":payload
};
}
}