1use chrono::Local;
2use json::{JsonValue, object};
3use log::info;
4use crate::base64::{url_decode, url_encode};
5use crate::hmac::str_to_hmacsha256;
6
7pub struct Jwt {
8 header: JsonValue,
9 payload: JsonValue,
10}
11
12impl Jwt {
13 pub fn default(iss: &str) -> Self {
17 Self {
18 header: object! {
19 "alg":"HS256",
20 "typ": "JWT"
21 },
22 payload: object! {
23 "iss":iss.clone(),
24 "iat":Local::now().timestamp(),
25 "jti":uuid::Uuid::new_v4().to_string()
26 },
27 }
28 }
29 pub fn payload(&mut self, sub: &str, aud: &str, custom: JsonValue) -> &mut Self {
35 self.payload["sub"] = sub.into();
36 self.payload["aud"] = aud.into();
37 self.payload["custom"] = custom.into();
38 self
39 }
40 pub fn nbf_exp(&mut self, nbf: i64, exp: i64) -> &mut Self {
45 if nbf == 0 {
46 self.payload["nbf"] = (Local::now().timestamp()).into();
47 } else {
48 self.payload["nbf"] = nbf.into();
49 }
50 let nbf = self.payload["nbf"].as_i64().unwrap();
51 let exp = 60 * exp * 1000;
52 self.payload["exp"] = (nbf + exp).into();
53 self
54 }
55 pub fn sign(&mut self, key: &str) -> String {
59 let header = url_encode(self.header.to_string());
60 let payload = url_encode(self.payload.to_string());
61 let signature = format!("{}.{}", header, payload);
62 let data = str_to_hmacsha256(key.clone(), signature.as_str().clone());
63 let sign = url_encode(data.to_string());
64 let token = format!("{}.{}.{}", header, payload, sign);
65 return token;
66 }
67 pub fn verify(token: &str, key: &str) -> bool {
69 let binding = token.to_string();
70 let data: Vec<&str> = binding.split(".").collect();
71 if data.len() < 3 {
72 return false;
73 }
74 let header = data[0].clone();
75 let payload = data[1].clone();
76 let signature = format!("{}.{}", header, payload);
77 let sign_old = data[2].clone();
78
79 let header = url_decode(header.to_string());
80 let header = json::parse(header.as_str()).unwrap();
81 let alg = header["alg"].as_str().unwrap();
82 let sign_new = {
83 match alg {
84 "HS256" => {
85 let hmacsha256 = str_to_hmacsha256(key.clone(), signature.as_str().clone());
86 url_encode(hmacsha256)
87 }
88 _ => {
89 signature
90 }
91 }
92 };
93 if sign_new != sign_old {
94 return false;
95 }
96 let payload_text = url_decode(payload.to_string());
97 match json::parse(payload_text.as_str()) {
98 Ok(payload) => {
99 let time = Local::now().timestamp();
100 let nbf = payload["nbf"].as_i64().unwrap();
102 if nbf > time {
103 return false;
104 }
105 let exp = payload["exp"].as_i64().unwrap();
107 if time > exp {
108 return false;
109 }
110 return true;
111 }
112 Err(e) => {
113 info!("{}",e);
114 info!("payload_old:{}",payload);
115 info!("payload_new:{}",payload_text);
116 return false;
117 }
118 }
119 }
120 pub fn info(token: &str) -> JsonValue {
122 let binding = token.to_string();
123 let data: Vec<&str> = binding.split(".").collect();
124 let header = data[0].clone();
125 let payload = data[1].clone();
126 let header = url_decode(header.to_string());
127 let header = {
128 let res = json::parse(header.as_str());
129 match res {
130 Ok(e) => e,
131 Err(_) => object! {}
132 }
133 };
134 let payload = url_decode(payload.to_string());
135 let payload = {
136 let res = json::parse(payload.as_str());
137 match res {
138 Ok(e) => e,
139 Err(_) => object! {}
140 }
141 };
142 return object! {
143 "header":header,
144 "payload":payload
145 };
146 }
147}