sa_token_core/token/
mod.rs1use std::sync::{Arc, OnceLock};
6
7use chrono::{DateTime, Utc};
8use serde::{Deserialize, Serialize};
9
10mod csprng;
11pub mod generator;
12pub mod jwt;
13pub mod map;
14pub mod validator;
15
16pub(crate) use csprng::random_hex;
17pub use generator::{TokenGenerator, generate_unique};
18pub use jwt::{JwtAlgorithm, JwtClaims, JwtManager};
19pub use validator::TokenValidator;
20
21#[derive(Debug, Clone, PartialEq, Eq, Hash)]
24pub struct TokenValue(Arc<str>);
25
26impl TokenValue {
27 pub fn new(value: impl AsRef<str>) -> Self {
29 Self(Arc::from(value.as_ref()))
30 }
31
32 #[inline]
34 pub fn as_str(&self) -> &str {
35 &self.0
36 }
37}
38
39impl Serialize for TokenValue {
40 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
41 serializer.serialize_str(&self.0)
42 }
43}
44
45impl<'de> Deserialize<'de> for TokenValue {
46 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
47 let s = String::deserialize(deserializer)?;
48 Ok(Self(Arc::from(s)))
49 }
50}
51
52impl From<String> for TokenValue {
53 fn from(s: String) -> Self {
54 Self(Arc::from(s))
55 }
56}
57
58impl From<&str> for TokenValue {
59 fn from(s: &str) -> Self {
60 Self(Arc::from(s))
61 }
62}
63
64impl From<TokenValue> for String {
65 fn from(v: TokenValue) -> Self {
66 v.0.to_string()
67 }
68}
69
70impl std::fmt::Display for TokenValue {
71 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
72 f.write_str(&self.0)
73 }
74}
75
76pub fn intern_login_type(s: &str) -> Arc<str> {
79 if s.is_empty() || s == crate::keys::LOGIN_TYPE_DEFAULT || s == "login" {
80 static DEFAULT: OnceLock<Arc<str>> = OnceLock::new();
81 return DEFAULT
82 .get_or_init(|| Arc::from(crate::keys::LOGIN_TYPE_DEFAULT))
83 .clone();
84 }
85 Arc::from(s)
86}
87
88mod arc_str_serde {
91 use serde::{Deserialize, Deserializer, Serializer};
92 use std::sync::Arc;
93
94 pub(super) fn serialize<S: Serializer>(
95 value: &Arc<str>,
96 serializer: S,
97 ) -> Result<S::Ok, S::Error> {
98 serializer.serialize_str(value)
99 }
100
101 pub(super) fn deserialize<'de, D: Deserializer<'de>>(
102 deserializer: D,
103 ) -> Result<Arc<str>, D::Error> {
104 let s = String::deserialize(deserializer)?;
105 Ok(Arc::from(s))
106 }
107}
108
109#[derive(Debug, Clone, Serialize, Deserialize)]
127pub struct TokenInfo {
128 pub token: TokenValue,
130
131 #[serde(with = "arc_str_serde")]
134 pub login_id: Arc<str>,
135
136 #[serde(with = "arc_str_serde")]
139 pub login_type: Arc<str>,
140
141 pub create_time: DateTime<Utc>,
143
144 pub last_active_time: DateTime<Utc>,
146
147 pub expire_time: Option<DateTime<Utc>>,
149
150 pub device: Option<String>,
152
153 pub extra_data: Option<serde_json::Value>,
155
156 pub nonce: Option<String>,
158
159 pub refresh_token: Option<String>,
161
162 pub refresh_token_expire_time: Option<DateTime<Utc>>,
164
165 #[serde(default)]
168 pub active_timeout_override: Option<i64>,
169}
170
171impl TokenInfo {
172 pub fn new(token: TokenValue, login_id: impl AsRef<str>) -> Self {
174 let now = Utc::now();
175 Self {
176 token,
177 login_id: Arc::from(login_id.as_ref()),
178 login_type: intern_login_type(crate::keys::LOGIN_TYPE_DEFAULT),
179 create_time: now,
180 last_active_time: now,
181 expire_time: None,
182 device: None,
183 extra_data: None,
184 nonce: None,
185 refresh_token: None,
186 refresh_token_expire_time: None,
187 active_timeout_override: None,
188 }
189 }
190
191 pub fn effective_active_timeout(&self, config: &crate::config::SaTokenConfig) -> i64 {
194 if config.dynamic_active_timeout {
195 self.active_timeout_override
196 .unwrap_or(config.active_timeout)
197 } else {
198 config.active_timeout
199 }
200 }
201
202 pub fn is_expired(&self) -> bool {
204 if let Some(expire_time) = self.expire_time {
205 Utc::now() > expire_time
206 } else {
207 false
208 }
209 }
210
211 pub fn update_active_time(&mut self) {
213 self.last_active_time = Utc::now();
214 }
215
216 pub fn is_freeze(&self, active_timeout: i64) -> bool {
221 if active_timeout <= 0 {
222 return false;
223 }
224 Utc::now()
225 .signed_duration_since(self.last_active_time)
226 .num_seconds()
227 > active_timeout
228 }
229}
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234
235 #[test]
236 fn test_is_freeze_respects_active_timeout() {
237 let mut info = TokenInfo::new(TokenValue::new("t"), "u");
238 info.last_active_time = Utc::now() - chrono::Duration::seconds(120);
239 assert!(info.is_freeze(60));
240 assert!(!info.is_freeze(-1));
241 assert!(!info.is_freeze(0));
242 }
243}
244
245#[derive(Debug, Clone)]
247pub struct TokenSign {
248 pub value: String,
250 pub device: Option<String>,
252}