Skip to main content

sa_token_core/
distributed.rs

1//! Distributed Session Management Module | 分布式 Session 管理模块
2//!
3//! # Overview | 概述
4//!
5//! This module enables **distributed session management** for microservices architecture,
6//! allowing multiple services to share authentication sessions seamlessly.
7//! 本模块为微服务架构提供**分布式 Session 管理**,允许多个服务无缝共享认证会话。
8//!
9//! ## Architecture Context | 架构上下文
10//!
11//! ```text
12//! ┌────────────────────────────────────────────────────────────────────┐
13//! │                   Microservices Architecture                       │
14//! │                   微服务架构                                        │
15//! └────────────────────────────────────────────────────────────────────┘
16//!
17//!    ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
18//!    │  Service A   │  │  Service B   │  │  Service C   │
19//!    │  (User API)  │  │  (Order API) │  │  (Pay API)   │
20//!    └──────┬───────┘  └──────┬───────┘  └──────┬───────┘
21//!           │                  │                  │
22//!           └──────────────────┼──────────────────┘
23//!                              │
24//!                    ┌─────────▼──────────┐
25//!                    │  Distributed       │
26//!                    │  Session Storage   │
27//!                    │  (Redis/Database)  │
28//!                    └────────────────────┘
29//!
30//! Each service can:
31//! 每个服务可以:
32//!   1. Create sessions for users
33//!      为用户创建会话
34//!   2. Access sessions created by other services
35//!      访问其他服务创建的会话
36//!   3. Share user authentication state
37//!      共享用户认证状态
38//! ```
39//!
40//! ## Key Use Cases | 关键使用场景
41//!
42//! ### 1. Single Sign-On (SSO) Across Services | 跨服务单点登录
43//!
44//! ```text
45//! Scenario: User logs in to Service A and accesses Service B
46//! 场景:用户登录服务 A 并访问服务 B
47//!
48//! 1. User → Service A: Login
49//!    用户 → 服务 A:登录
50//!    ├─ Service A creates session: session_id = "abc123"
51//!    │  服务 A 创建会话:session_id = "abc123"
52//!    └─ Saves to distributed storage
53//!       保存到分布式存储
54//!
55//! 2. User → Service B: Request with session_id = "abc123"
56//!    用户 → 服务 B:请求带 session_id = "abc123"
57//!    ├─ Service B retrieves session from storage
58//!    │  服务 B 从存储中获取会话
59//!    ├─ Validates user is authenticated
60//!    │  验证用户已认证
61//!    └─ Processes request ✅
62//!       处理请求 ✅
63//!
64//! No need to log in again! 无需再次登录!
65//! ```
66//!
67//! ### 2. Session Sharing for User Context | 会话共享用户上下文
68//!
69//! ```text
70//! Service A stores: { "user_role": "admin", "department": "IT" }
71//! 服务 A 存储:{ "user_role": "admin", "department": "IT" }
72//!
73//! Service B reads: Same session attributes available
74//! 服务 B 读取:相同的会话属性可用
75//!
76//! Service C updates: { "last_order": "order_123" }
77//! 服务 C 更新:{ "last_order": "order_123" }
78//!
79//! All services share the same session state!
80//! 所有服务共享相同的会话状态!
81//! ```
82//!
83//! ### 3. Multi-Device Session Management | 多设备会话管理
84//!
85//! ```text
86//! User: user_123
87//!   ├─ Session 1: Web (Service A)
88//!   │  会话 1:网页(服务 A)
89//!   ├─ Session 2: Mobile (Service B)
90//!   │  会话 2:移动端(服务 B)
91//!   └─ Session 3: Desktop (Service C)
92//!      会话 3:桌面端(服务 C)
93//!
94//! All sessions can be:
95//! 所有会话可以:
96//!   - Listed: get_sessions_by_login_id()
97//!   - Managed individually
98//!   - Terminated all at once: delete_all_sessions()
99//! ```
100//!
101//! ## Integration with Sa-Token | 与 Sa-Token 的集成
102//!
103//! ```text
104//! ┌─────────────────────────────────────────────────────────┐
105//! │               Sa-Token Core Flow                        │
106//! │               Sa-Token 核心流程                          │
107//! └─────────────────────────────────────────────────────────┘
108//!
109//! SaTokenManager::login()
110//!   ├─ 1. Generate token
111//!   │     生成 token
112//!   ├─ 2. Create TokenInfo
113//!   │     创建 TokenInfo
114//!   └─ 3. Create DistributedSession (if enabled)
115//!          创建 DistributedSession(如果启用)
116//!          ├─ session_id: UUID
117//!          ├─ login_id: user's login ID
118//!          ├─ token: access token
119//!          ├─ service_id: current service
120//!          └─ attributes: custom data
121//!
122//! StpUtil::get_session()
123//!   └─ Retrieves distributed session
124//!      获取分布式会话
125//!
126//! StpUtil::logout()
127//!   └─ Deletes distributed session(s)
128//!      删除分布式会话
129//! ```
130
131//!
132//! ## Workflow Diagrams | 工作流程图
133//!
134//! ### Complete Session Lifecycle | 完整会话生命周期
135//!
136//! ```text
137//! ┌──────────────────────────────────────────────────────────────────┐
138//! │                    Session Lifecycle                             │
139//! │                    会话生命周期                                   │
140//! └──────────────────────────────────────────────────────────────────┘
141//!
142//! User                Service A           Storage           Service B
143//! 用户                服务 A              存储              服务 B
144//!  │                     │                   │                   │
145//!  │  1. Login           │                   │                   │
146//!  │  登录               │                   │                   │
147//!  ├────────────────────▶│                   │                   │
148//!  │                     │  2. create_session()                  │
149//!  │                     │  创建会话          │                   │
150//!  │                     │  ├─ session_id: uuid                  │
151//!  │                     │  ├─ login_id: user_123                │
152//!  │                     │  ├─ token: access_token               │
153//!  │                     │  └─ service_id: service-a             │
154//!  │                     │                   │                   │
155//!  │                     │  3. save_session()│                   │
156//!  │                     │  保存会话          │                   │
157//!  │                     ├──────────────────▶│                   │
158//!  │                     │                   │  Store with TTL   │
159//!  │                     │                   │  存储并设置 TTL    │
160//!  │                     │                   │                   │
161//!  │  4. session_id      │                   │                   │
162//!  │  返回会话 ID        │                   │                   │
163//!  │◀────────────────────│                   │                   │
164//!  │                     │                   │                   │
165//!  │  5. Request to Service B with session_id                    │
166//!  │  带 session_id 请求服务 B                                    │
167//!  ├────────────────────────────────────────────────────────────▶│
168//!  │                     │                   │                   │
169//!  │                     │                   │  6. get_session() │
170//!  │                     │                   │  获取会话          │
171//!  │                     │                   │◀──────────────────│
172//!  │                     │                   │                   │
173//!  │                     │                   │  7. Return session│
174//!  │                     │                   │  返回会话数据      │
175//!  │                     │                   ├──────────────────▶│
176//!  │                     │                   │                   │
177//!  │                     │                   │  8. refresh_session()
178//!  │                     │                   │  刷新会话          │
179//!  │                     │                   │  (update last_access)
180//!  │                     │                   │◀──────────────────│
181//!  │                     │                   │                   │
182//!  │  9. Response        │                   │                   │
183//!  │  响应               │                   │                   │
184//!  │◀────────────────────────────────────────────────────────────│
185//!  │                     │                   │                   │
186//!  │  10. Logout         │                   │                   │
187//!  │  登出               │                   │                   │
188//!  ├────────────────────▶│                   │                   │
189//!  │                     │  11. delete_session()                 │
190//!  │                     │  删除会话          │                   │
191//!  │                     ├──────────────────▶│                   │
192//!  │                     │                   │  Remove from storage
193//!  │                     │                   │  从存储中移除      │
194//!  │                     │                   │                   │
195//!  │  12. Logout Success │                   │                   │
196//!  │  登出成功           │                   │                   │
197//!  │◀────────────────────│                   │                   │
198//!  │                     │                   │                   │
199//! ```
200//!
201//! ### Service Authentication Flow | 服务认证流程
202//!
203//! ```text
204//! ┌──────────────────────────────────────────────────────────────────┐
205//! │                Service Inter-Communication                       │
206//! │                服务间通信                                         │
207//! └──────────────────────────────────────────────────────────────────┘
208//!
209//! Service B          Service A (Session Manager)          Storage
210//! 服务 B             服务 A(会话管理器)                  存储
211//!   │                        │                               │
212//!   │  1. Register           │                               │
213//!   │  注册服务              │                               │
214//!   │  ├─ service_id         │                               │
215//!   │  ├─ service_name       │                               │
216//!   │  ├─ secret_key         │                               │
217//!   │  └─ permissions        │                               │
218//!   ├───────────────────────▶│                               │
219//!   │                        │  Store credentials            │
220//!   │                        │  存储凭证                      │
221//!   │                        │  (in memory)                  │
222//!   │                        │                               │
223//!   │  2. Registered ✅      │                               │
224//!   │◀───────────────────────│                               │
225//!   │                        │                               │
226//!   │  3. Access session     │                               │
227//!   │  访问会话              │                               │
228//!   │  ├─ service_id         │                               │
229//!   │  ├─ secret_key         │                               │
230//!   │  └─ session_id         │                               │
231//!   ├───────────────────────▶│                               │
232//!   │                        │  4. verify_service()          │
233//!   │                        │  验证服务                      │
234//!   │                        │  ├─ Lookup service            │
235//!   │                        │  └─ Compare secret_key        │
236//!   │                        │                               │
237//!   │                        │  5. get_session()             │
238//!   │                        │  获取会话                      │
239//!   │                        ├──────────────────────────────▶│
240//!   │                        │                               │
241//!   │                        │  6. Return session            │
242//!   │                        │  返回会话                      │
243//!   │                        │◀──────────────────────────────│
244//!   │                        │                               │
245//!   │  7. Session data ✅    │                               │
246//!   │◀───────────────────────│                               │
247//!   │                        │                               │
248//! ```
249//!
250//! ## Storage Backends | 存储后端
251//!
252//! The module is storage-agnostic. You can implement custom backends:
253//! 本模块与存储无关。您可以实现自定义后端:
254//!
255//! ### Redis Implementation (Recommended) | Redis 实现(推荐)
256//!
257//! ```rust,ignore
258//! use redis::AsyncCommands;
259//!
260//! pub struct RedisDistributedStorage {
261//!     client: redis::Client,
262//! }
263//!
264//! #[async_trait]
265//! impl DistributedSessionStorage for RedisDistributedStorage {
266//!     async fn save_session(&self, session: DistributedSession, ttl: Option<Duration>)
267//!         -> Result<(), SaTokenError>
268//!     {
269//!         let mut conn = self.client.get_async_connection().await?;
270//!         let key = format!("distributed:session:{}", session.session_id);
271//!         // Prefer SharedSerializer / SaTokenConfig::encode (A2), not hardcoded JSON.
272//!         // 优先用 SharedSerializer / SaTokenConfig::encode(A2),勿硬编码 JSON。
273//!         let value = serializer.encode(&session)?;
274//!         if let Some(ttl) = ttl {
275//!             conn.set_ex(&key, value, ttl.as_secs() as usize).await?;
276//!         } else {
277//!             conn.set(&key, value).await?;
278//!         }
279//!         
280//!         // Index by login_id
281//!         let index_key = format!("distributed:login:{}", session.login_id);
282//!         conn.sadd(index_key, &session.session_id).await?;
283//!         
284//!         Ok(())
285//!     }
286//!     
287//!     // ... other methods
288//! }
289//! ```
290//!
291//! ### Database Implementation | 数据库实现
292//!
293//! ```rust,ignore
294//! use sqlx::PgPool;
295//!
296//! pub struct PostgresDistributedStorage {
297//!     pool: PgPool,
298//! }
299//!
300//! #[async_trait]
301//! impl DistributedSessionStorage for PostgresDistributedStorage {
302//!     async fn save_session(&self, session: DistributedSession, ttl: Option<Duration>)
303//!         -> Result<(), SaTokenError>
304//!     {
305//!         let expires_at = ttl.map(|t| Utc::now() + chrono::Duration::from_std(t).unwrap());
306//!         
307//!         sqlx::query!(
308//!             "INSERT INTO distributed_sessions
309//!              (session_id, login_id, token, service_id, attributes, expires_at)
310//!              VALUES ($1, $2, $3, $4, $5, $6)
311//!              ON CONFLICT (session_id) DO UPDATE
312//!              SET attributes = $5, last_access = NOW()",
313//!             session.session_id,
314//!             session.login_id,
315//!             session.token,
316//!             session.service_id,
317//!             serde_json::to_value(&session.attributes)?,
318//!             expires_at,
319//!         )
320//!         .execute(&self.pool)
321//!         .await?;
322//!         
323//!         Ok(())
324//!     }
325//!     
326//!     // ... other methods
327//! }
328//! ```
329//!
330//! ## Best Practices | 最佳实践
331//!
332//! ### 1. Service Registration | 服务注册
333//!
334//! ```rust,ignore
335//! // Initialize each service with unique credentials
336//! // 为每个服务初始化唯一凭证
337//! let credential = ServiceCredential {
338//!     service_id: "user-service".to_string(),
339//!     service_name: "User Management Service".to_string(),
340//!     secret_key: generate_secure_secret(), // Use crypto-secure generation
341//!     created_at: Utc::now(),
342//!     permissions: vec!["user.read".to_string(), "user.write".to_string()],
343//! };
344//! manager.register_service(credential).await;
345//! ```
346//!
347//! ### 2. Session Creation with Context | 带上下文的会话创建
348//!
349//! ```rust,ignore
350//! // Create session with user context
351//! // 创建带用户上下文的会话
352//! let session = manager.create_session(login_id, token).await?;
353//!
354//! // Add relevant attributes immediately
355//! // 立即添加相关属性
356//! manager.set_attribute(&session.session_id, "user_role".to_string(), "admin".to_string()).await?;
357//! manager.set_attribute(&session.session_id, "department".to_string(), "IT".to_string()).await?;
358//! manager.set_attribute(&session.session_id, "login_device".to_string(), "web".to_string()).await?;
359//! ```
360//!
361//! ### 3. Cross-Service Access Pattern | 跨服务访问模式
362//!
363//! ```rust,ignore
364//! // Service B accesses session created by Service A
365//! // 服务 B 访问服务 A 创建的会话
366//!
367//! // 1. Verify service identity
368//! // 验证服务身份
369//! let service_cred = manager.verify_service("service-b", request.secret).await?;
370//!
371//! // 2. Check permissions
372//! // 检查权限
373//! if !service_cred.permissions.contains(&"session.read".to_string()) {
374//!     return Err(SaTokenError::PermissionDenied);
375//! }
376//!
377//! // 3. Access session
378//! // 访问会话
379//! let session = manager.get_session(&request.session_id).await?;
380//!
381//! // 4. Refresh to keep session alive
382//! // 刷新以保持会话活跃
383//! manager.refresh_session(&session.session_id).await?;
384//! ```
385//!
386//! ### 4. Multi-Device Logout | 多设备登出
387//!
388//! ```rust,ignore
389//! // Logout from all devices
390//! // 从所有设备登出
391//! manager.delete_all_sessions(&login_id).await?;
392//!
393//! // Or logout specific session
394//! // 或登出特定会话
395//! manager.delete_session(&session_id).await?;
396//! ```
397//!
398//! ### 5. Session Monitoring | 会话监控
399//!
400//! ```rust,ignore
401//! // Monitor user's active sessions
402//! // 监控用户的活跃会话
403//! let sessions = manager.get_sessions_by_login_id(&login_id).await?;
404//!
405//! for session in sessions {
406//!     println!("Session: {} from service: {}, last active: {}",
407//!         session.session_id,
408//!         session.service_id,
409//!         session.last_access
410//!     );
411//!     
412//!     // Check for suspicious activity
413//!     // 检查可疑活动
414//!     if is_suspicious(&session) {
415//!         manager.delete_session(&session.session_id).await?;
416//!     }
417//! }
418//! ```
419//!
420//! ## Security Considerations | 安全考虑
421//!
422//! ```text
423//! 1. ✅ Service Authentication | 服务认证
424//!    - Each service has unique secret_key
425//!    - Verify credentials before granting access
426//!    - Rotate keys periodically
427//!
428//! 2. ✅ Permission-Based Access | 基于权限的访问
429//!    - Services have explicit permissions
430//!    - Check permissions before operations
431//!    - Implement least-privilege principle
432//!
433//! 3. ✅ Session Timeout | 会话超时
434//!    - Configure appropriate TTL
435//!    - Auto-expire inactive sessions
436//!    - Refresh on active use
437//!
438//! 4. ✅ Data Encryption | 数据加密
439//!    - Encrypt sensitive session attributes
440//!    - Use TLS for inter-service communication
441//!    - Encrypt data at rest in storage
442//!
443//! 5. ✅ Audit Logging | 审计日志
444//!    - Log session creation/deletion
445//!    - Track cross-service access
446//!    - Monitor for anomalies
447//! ```
448
449use crate::config::SaTokenConfig;
450use crate::dao::SaTokenDao;
451use crate::error::SaTokenError;
452use async_trait::async_trait;
453use chrono::{DateTime, Utc};
454use sa_token_adapter::storage::SaStorage;
455use serde::{Deserialize, Serialize};
456use std::collections::HashMap;
457use std::sync::Arc;
458use std::time::Duration;
459use tokio::sync::RwLock;
460
461/// Distributed session data structure
462/// 分布式 Session 数据结构
463///
464/// Represents a session that can be shared across multiple services
465/// 表示可以在多个服务之间共享的 Session
466#[derive(Debug, Clone, Serialize, Deserialize)]
467pub struct DistributedSession {
468    /// Unique session identifier | 唯一 Session 标识符
469    pub session_id: String,
470
471    /// User login ID | 用户登录 ID
472    pub login_id: String,
473
474    /// Authentication token | 认证 Token
475    pub token: String,
476
477    /// ID of the service that created this session | 创建此 Session 的服务 ID
478    pub service_id: String,
479
480    /// Session creation time | Session 创建时间
481    pub create_time: DateTime<Utc>,
482
483    /// Last access time | 最后访问时间
484    pub last_access: DateTime<Utc>,
485
486    /// Session attributes (key-value pairs) | Session 属性(键值对)
487    pub attributes: HashMap<String, String>,
488}
489
490/// Service credential for inter-service authentication
491/// 服务间认证的服务凭证
492///
493/// Contains service identification and permission information
494/// 包含服务标识和权限信息
495#[derive(Debug, Clone, Serialize, Deserialize)]
496pub struct ServiceCredential {
497    /// Unique service identifier | 唯一服务标识符
498    pub service_id: String,
499
500    /// Human-readable service name | 可读的服务名称
501    pub service_name: String,
502
503    /// Service authentication secret key | 服务认证密钥
504    pub secret_key: String,
505
506    /// Service registration time | 服务注册时间
507    pub created_at: DateTime<Utc>,
508
509    /// List of permissions this service has | 该服务拥有的权限列表
510    pub permissions: Vec<String>,
511}
512
513/// Distributed session storage trait
514/// 分布式 Session 存储 trait
515///
516/// Implement this trait to provide custom storage backends
517/// 实现此 trait 以提供自定义存储后端
518#[async_trait]
519pub trait DistributedSessionStorage: Send + Sync {
520    /// Save a session to storage with optional TTL
521    /// 保存 Session 到存储,可选 TTL
522    ///
523    /// # Arguments | 参数
524    /// * `session` - Session to save | 要保存的 Session
525    /// * `ttl` - Time-to-live duration | 生存时间
526    async fn save_session(
527        &self,
528        session: DistributedSession,
529        ttl: Option<Duration>,
530    ) -> Result<(), SaTokenError>;
531
532    /// Get a session from storage
533    /// 从存储获取 Session
534    ///
535    /// # Arguments | 参数
536    /// * `session_id` - Session identifier | Session 标识符
537    async fn get_session(
538        &self,
539        session_id: &str,
540    ) -> Result<Option<DistributedSession>, SaTokenError>;
541
542    /// Delete a session from storage
543    /// 从存储删除 Session
544    ///
545    /// # Arguments | 参数
546    /// * `session_id` - Session identifier | Session 标识符
547    async fn delete_session(&self, session_id: &str) -> Result<(), SaTokenError>;
548
549    /// Get all sessions for a specific user
550    /// 获取特定用户的所有 Sessions
551    ///
552    /// # Arguments | 参数
553    /// * `login_id` - User login ID | 用户登录 ID
554    async fn get_sessions_by_login_id(
555        &self,
556        login_id: &str,
557    ) -> Result<Vec<DistributedSession>, SaTokenError>;
558
559    /// 保存服务凭证 | Save a service credential
560    /// 用于把 register_service 的凭证持久化到存储
561    async fn save_credential(&self, credential: ServiceCredential) -> Result<(), SaTokenError>;
562
563    /// 按 service_id 获取服务凭证 | Get a service credential by service_id
564    /// 未找到返回 Ok(None)
565    async fn get_credential(
566        &self,
567        service_id: &str,
568    ) -> Result<Option<ServiceCredential>, SaTokenError>;
569}
570
571/// Distributed session manager
572/// 分布式 Session 管理器
573///
574/// Manages distributed sessions and service authentication
575/// 管理分布式 Sessions 和服务认证
576pub struct DistributedSessionManager {
577    /// Session 存储后端
578    storage: Arc<dyn DistributedSessionStorage>,
579    /// 当前服务 ID
580    service_id: String,
581    /// 默认 Session 超时时间
582    session_timeout: Duration,
583}
584
585impl std::fmt::Debug for DistributedSessionManager {
586    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
587        f.write_str("DistributedSessionManager { .. }")
588    }
589}
590
591impl DistributedSessionManager {
592    /// Create a new distributed session manager
593    /// 创建新的分布式 Session 管理器
594    ///
595    /// # Arguments | 参数
596    /// * `storage` - Session storage implementation | Session 存储实现
597    /// * `service_id` - ID of this service | 此服务的 ID
598    /// * `session_timeout` - Default session timeout | 默认 Session 超时时间
599    ///
600    /// # Example | 示例
601    /// ```rust,ignore
602    /// let storage = Arc::new(MyDistributedStorage::new());
603    /// let manager = DistributedSessionManager::new(
604    ///     storage,
605    ///     "my-service".to_string(),
606    ///     Duration::from_secs(3600),
607    /// );
608    /// ```
609    pub fn new(
610        storage: Arc<dyn DistributedSessionStorage>,
611        service_id: String,
612        session_timeout: Duration,
613    ) -> Self {
614        Self {
615            storage,
616            service_id,
617            session_timeout,
618        }
619    }
620
621    /// 注册服务凭证(持久化到底层存储)
622    /// 返回 Result 以便调用方处理存储错误
623    pub async fn register_service(
624        &self,
625        credential: ServiceCredential,
626    ) -> Result<(), SaTokenError> {
627        self.storage.save_credential(credential).await
628    }
629
630    /// Verify a service's credentials
631    /// 验证服务的凭证
632    ///
633    /// # Arguments | 参数
634    /// * `service_id` - Service identifier | 服务标识符
635    /// * `secret` - Service secret key | 服务密钥
636    ///
637    /// # Returns | 返回值
638    /// * `Ok(ServiceCredential)` - Service authenticated | 服务已认证
639    /// * `Err(PermissionDenied)` - Invalid credentials | 凭证无效
640    ///
641    /// # Example | 示例
642    /// ```rust,ignore
643    /// match manager.verify_service("api-gateway", "secret123").await {
644    ///     Ok(cred) => println!("Service {} verified", cred.service_name),
645    ///     Err(e) => println!("Verification failed: {}", e),
646    /// }
647    /// ```
648    /// 校验服务凭证
649    /// service_id 存在且 secret_key 匹配时返回凭证,否则返回 PermissionDenied
650    pub async fn verify_service(
651        &self,
652        service_id: &str,
653        secret: &str,
654    ) -> Result<ServiceCredential, SaTokenError> {
655        if let Some(cred) = self.storage.get_credential(service_id).await?
656            && crate::http_basic::ct_eq(cred.secret_key.as_bytes(), secret.as_bytes())
657        {
658            return Ok(cred);
659        }
660        Err(SaTokenError::PermissionDenied)
661    }
662
663    /// Create a new distributed session
664    /// 创建新的分布式 Session
665    ///
666    /// # Arguments | 参数
667    /// * `login_id` - User login ID | 用户登录 ID
668    /// * `token` - Authentication token | 认证 Token
669    ///
670    /// # Returns | 返回值
671    /// * `Ok(DistributedSession)` - Session created | Session 已创建
672    /// * `Err(SaTokenError)` - Creation failed | 创建失败
673    ///
674    /// # Example | 示例
675    /// ```rust,ignore
676    /// let session = manager.create_session(
677    ///     "user123".to_string(),
678    ///     "token456".to_string(),
679    /// ).await?;
680    /// println!("Session created: {}", session.session_id);
681    /// ```
682    pub async fn create_session(
683        &self,
684        login_id: String,
685        token: String,
686    ) -> Result<DistributedSession, SaTokenError> {
687        let session = DistributedSession {
688            session_id: uuid::Uuid::new_v4().to_string(),
689            login_id,
690            token,
691            service_id: self.service_id.clone(),
692            create_time: Utc::now(),
693            last_access: Utc::now(),
694            attributes: HashMap::new(),
695        };
696
697        self.storage
698            .save_session(session.clone(), Some(self.session_timeout))
699            .await?;
700        Ok(session)
701    }
702
703    /// Get a session by ID
704    /// 通过 ID 获取 Session
705    ///
706    /// # Arguments | 参数
707    /// * `session_id` - Session identifier | Session 标识符
708    ///
709    /// # Returns | 返回值
710    /// * `Ok(DistributedSession)` - Session found | 找到 Session
711    /// * `Err(SessionNotFound)` - Session not found | 未找到 Session
712    ///
713    /// # Example | 示例
714    /// ```rust,ignore
715    /// let session = manager.get_session("session-id-123").await?;
716    /// println!("User: {}", session.login_id);
717    /// ```
718    pub async fn get_session(&self, session_id: &str) -> Result<DistributedSession, SaTokenError> {
719        self.storage
720            .get_session(session_id)
721            .await?
722            .ok_or(SaTokenError::SessionNotFound)
723    }
724
725    /// Update an existing session
726    /// 更新现有 Session
727    ///
728    /// # Arguments | 参数
729    /// * `session` - Updated session data | 更新后的 Session 数据
730    ///
731    /// # Example | 示例
732    /// ```rust,ignore
733    /// let mut session = manager.get_session("session-id").await?;
734    /// session.attributes.insert("role".to_string(), "admin".to_string());
735    /// manager.update_session(session).await?;
736    /// ```
737    pub async fn update_session(&self, session: DistributedSession) -> Result<(), SaTokenError> {
738        self.storage
739            .save_session(session, Some(self.session_timeout))
740            .await
741    }
742
743    /// Delete a session
744    /// 删除 Session
745    ///
746    /// # Arguments | 参数
747    /// * `session_id` - Session identifier | Session 标识符
748    ///
749    /// # Example | 示例
750    /// ```rust,ignore
751    /// manager.delete_session("session-id-123").await?;
752    /// ```
753    pub async fn delete_session(&self, session_id: &str) -> Result<(), SaTokenError> {
754        self.storage.delete_session(session_id).await
755    }
756
757    /// Refresh a session (update last access time)
758    /// 刷新 Session(更新最后访问时间)
759    ///
760    /// # Arguments | 参数
761    /// * `session_id` - Session identifier | Session 标识符
762    ///
763    /// # Example | 示例
764    /// ```rust,ignore
765    /// manager.refresh_session("session-id-123").await?;
766    /// ```
767    pub async fn refresh_session(&self, session_id: &str) -> Result<(), SaTokenError> {
768        let mut session = self.get_session(session_id).await?;
769        session.last_access = Utc::now();
770        self.update_session(session).await
771    }
772
773    /// Set a session attribute
774    /// 设置 Session 属性
775    ///
776    /// # Arguments | 参数
777    /// * `session_id` - Session identifier | Session 标识符
778    /// * `key` - Attribute key | 属性键
779    /// * `value` - Attribute value | 属性值
780    ///
781    /// # Example | 示例
782    /// ```rust,ignore
783    /// manager.set_attribute("session-id", "theme".to_string(), "dark".to_string()).await?;
784    /// ```
785    pub async fn set_attribute(
786        &self,
787        session_id: &str,
788        key: String,
789        value: String,
790    ) -> Result<(), SaTokenError> {
791        let mut session = self.get_session(session_id).await?;
792        session.attributes.insert(key, value);
793        session.last_access = Utc::now();
794        self.update_session(session).await
795    }
796
797    /// Get a session attribute
798    /// 获取 Session 属性
799    ///
800    /// # Arguments | 参数
801    /// * `session_id` - Session identifier | Session 标识符
802    /// * `key` - Attribute key | 属性键
803    ///
804    /// # Returns | 返回值
805    /// * `Some(value)` - Attribute found | 找到属性
806    /// * `None` - Attribute not found | 未找到属性
807    ///
808    /// # Example | 示例
809    /// ```rust,ignore
810    /// if let Some(theme) = manager.get_attribute("session-id", "theme").await? {
811    ///     println!("Theme: {}", theme);
812    /// }
813    /// ```
814    pub async fn get_attribute(
815        &self,
816        session_id: &str,
817        key: &str,
818    ) -> Result<Option<String>, SaTokenError> {
819        let session = self.get_session(session_id).await?;
820        Ok(session.attributes.get(key).cloned())
821    }
822
823    /// Remove a session attribute
824    /// 移除 Session 属性
825    ///
826    /// # Arguments | 参数
827    /// * `session_id` - Session identifier | Session 标识符
828    /// * `key` - Attribute key | 属性键
829    ///
830    /// # Example | 示例
831    /// ```rust,ignore
832    /// manager.remove_attribute("session-id", "temp_data").await?;
833    /// ```
834    pub async fn remove_attribute(&self, session_id: &str, key: &str) -> Result<(), SaTokenError> {
835        let mut session = self.get_session(session_id).await?;
836        session.attributes.remove(key);
837        session.last_access = Utc::now();
838        self.update_session(session).await
839    }
840
841    /// Get all sessions for a specific user
842    /// 获取特定用户的所有 Sessions
843    ///
844    /// # Arguments | 参数
845    /// * `login_id` - User login ID | 用户登录 ID
846    ///
847    /// # Returns | 返回值
848    /// Vector of sessions | Sessions 向量
849    ///
850    /// # Example | 示例
851    /// ```rust,ignore
852    /// let sessions = manager.get_sessions_by_login_id("user123").await?;
853    /// println!("User has {} active sessions", sessions.len());
854    /// ```
855    pub async fn get_sessions_by_login_id(
856        &self,
857        login_id: &str,
858    ) -> Result<Vec<DistributedSession>, SaTokenError> {
859        self.storage.get_sessions_by_login_id(login_id).await
860    }
861
862    /// Delete all sessions for a specific user
863    /// 删除特定用户的所有 Sessions
864    ///
865    /// # Arguments | 参数
866    /// * `login_id` - User login ID | 用户登录 ID
867    ///
868    /// # Example | 示例
869    /// ```rust,ignore
870    /// manager.delete_all_sessions("user123").await?;
871    /// ```
872    pub async fn delete_all_sessions(&self, login_id: &str) -> Result<(), SaTokenError> {
873        let sessions = self.get_sessions_by_login_id(login_id).await?;
874        for session in sessions {
875            self.delete_session(&session.session_id).await?;
876        }
877        Ok(())
878    }
879
880    /// Delete distributed sessions that carry this access token (one device).
881    /// 只删携带该 access token 的分布式会话(单设备),避免误杀其它终端。
882    pub async fn delete_sessions_by_token(
883        &self,
884        login_id: &str,
885        token: &str,
886    ) -> Result<(), SaTokenError> {
887        let sessions = self.storage.get_sessions_by_login_id(login_id).await?;
888        for session in sessions {
889            if crate::http_basic::ct_eq(session.token.as_bytes(), token.as_bytes()) {
890                self.storage.delete_session(&session.session_id).await?;
891            }
892        }
893        Ok(())
894    }
895}
896
897/// In-memory distributed session storage (process-local only; not cross-instance).
898/// 内存分布式 Session 存储(仅进程内,不能跨实例)。
899///
900/// For testing and local demos — do not use as shared multi-node storage.
901/// 用于测试与本地演示 —— 不可作为多节点共享存储。
902pub struct InMemoryDistributedStorage {
903    /// Sessions 存储: session_id -> DistributedSession
904    sessions: Arc<RwLock<HashMap<String, DistributedSession>>>,
905    /// 登录索引: login_id -> Vec<session_id>
906    login_index: Arc<RwLock<HashMap<String, Vec<String>>>>,
907    /// 服务凭证: service_id -> ServiceCredential
908    credentials: Arc<RwLock<HashMap<String, ServiceCredential>>>,
909}
910
911impl std::fmt::Debug for InMemoryDistributedStorage {
912    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
913        f.write_str("InMemoryDistributedStorage { .. }")
914    }
915}
916
917impl InMemoryDistributedStorage {
918    /// 创建新的内存存储
919    pub fn new() -> Self {
920        Self {
921            sessions: Arc::new(RwLock::new(HashMap::new())),
922            login_index: Arc::new(RwLock::new(HashMap::new())),
923            credentials: Arc::new(RwLock::new(HashMap::new())),
924        }
925    }
926}
927
928impl Default for InMemoryDistributedStorage {
929    fn default() -> Self {
930        Self::new()
931    }
932}
933
934#[async_trait]
935impl DistributedSessionStorage for InMemoryDistributedStorage {
936    /// Save session to memory storage | 保存会话到内存存储
937    ///
938    /// # Implementation Details | 实现细节
939    ///
940    /// 1. Stores session in main HashMap by session_id
941    ///    在主 HashMap 中按 session_id 存储会话
942    /// 2. Updates login_index for quick user lookup
943    ///    更新 login_index 以快速查找用户
944    ///
945    /// # Note | 注意
946    ///
947    /// TTL is ignored in memory storage (for simplicity).
948    /// In production, use Redis or similar with built-in TTL support.
949    /// 内存存储中忽略 TTL(为简化实现)。
950    /// 在生产环境中,使用 Redis 或类似的内置 TTL 支持的存储。
951    async fn save_session(
952        &self,
953        session: DistributedSession,
954        _ttl: Option<Duration>,
955    ) -> Result<(), SaTokenError> {
956        let session_id = session.session_id.clone();
957        let login_id = session.login_id.clone();
958
959        // 1. Store session in main map
960        // 在主映射中存储会话
961        let mut sessions = self.sessions.write().await;
962        sessions.insert(session_id.clone(), session);
963
964        // 2. Update login index for this user
965        // 更新此用户的登录索引
966        let mut index = self.login_index.write().await;
967        let session_list = index.entry(login_id).or_insert_with(Vec::new);
968
969        // Add only if not already present (prevent duplicates)
970        // 仅在不存在时添加(防止重复)
971        if !session_list.contains(&session_id) {
972            session_list.push(session_id);
973        }
974
975        Ok(())
976    }
977
978    /// Get session from memory storage | 从内存存储获取会话
979    ///
980    /// # Returns | 返回
981    ///
982    /// * `Ok(Some(session))` - Session found | 找到会话
983    /// * `Ok(None)` - Session not found | 未找到会话
984    async fn get_session(
985        &self,
986        session_id: &str,
987    ) -> Result<Option<DistributedSession>, SaTokenError> {
988        let sessions = self.sessions.read().await;
989        Ok(sessions.get(session_id).cloned())
990    }
991
992    /// Delete session from memory storage | 从内存存储删除会话
993    ///
994    /// # Implementation Details | 实现细节
995    ///
996    /// 1. Removes session from main HashMap
997    ///    从主 HashMap 中移除会话
998    /// 2. Removes session_id from login_index
999    ///    从 login_index 中移除 session_id
1000    /// 3. Cleans up empty index entries
1001    ///    清理空的索引条目
1002    async fn delete_session(&self, session_id: &str) -> Result<(), SaTokenError> {
1003        // 1. Remove from main storage and get session data
1004        // 从主存储中移除并获取会话数据
1005        let mut sessions = self.sessions.write().await;
1006        if let Some(session) = sessions.remove(session_id) {
1007            // 2. Update login index
1008            // 更新登录索引
1009            let mut index = self.login_index.write().await;
1010            if let Some(session_ids) = index.get_mut(&session.login_id) {
1011                // Remove this session_id from the list
1012                // 从列表中移除此 session_id
1013                session_ids.retain(|id| id != session_id);
1014
1015                // 3. Clean up: remove login_id entry if no sessions left
1016                // 清理:如果没有剩余会话,移除 login_id 条目
1017                if session_ids.is_empty() {
1018                    index.remove(&session.login_id);
1019                }
1020            }
1021        }
1022        Ok(())
1023    }
1024
1025    /// Get all sessions for a user | 获取用户的所有会话
1026    ///
1027    /// # Implementation Details | 实现细节
1028    ///
1029    /// 1. Looks up session_ids in login_index
1030    ///    在 login_index 中查找 session_ids
1031    /// 2. Retrieves full session data for each session_id
1032    ///    为每个 session_id 检索完整的会话数据
1033    /// 3. Filters out any missing sessions (cleanup)
1034    ///    过滤掉任何缺失的会话(清理)
1035    ///
1036    /// # Returns | 返回
1037    ///
1038    /// Vector of all active sessions for the user
1039    /// 用户所有活跃会话的向量
1040    async fn get_sessions_by_login_id(
1041        &self,
1042        login_id: &str,
1043    ) -> Result<Vec<DistributedSession>, SaTokenError> {
1044        // 1. Get session IDs from index
1045        // 从索引中获取会话 IDs
1046        let index = self.login_index.read().await;
1047        let session_ids = index.get(login_id).cloned().unwrap_or_default();
1048
1049        // 2. Retrieve full session data
1050        // 检索完整的会话数据
1051        let sessions = self.sessions.read().await;
1052        let mut result = Vec::new();
1053
1054        for session_id in session_ids {
1055            if let Some(session) = sessions.get(&session_id) {
1056                result.push(session.clone());
1057            }
1058            // Note: If session not found, it was deleted but index not updated
1059            // This is a minor inconsistency acceptable in memory storage
1060            // 注意:如果未找到会话,说明会话已删除但索引未更新
1061            // 这是内存存储中可接受的小不一致
1062        }
1063
1064        Ok(result)
1065    }
1066
1067    /// 保存服务凭证到内存
1068    async fn save_credential(&self, credential: ServiceCredential) -> Result<(), SaTokenError> {
1069        let mut creds = self.credentials.write().await;
1070        creds.insert(credential.service_id.clone(), credential);
1071        Ok(())
1072    }
1073
1074    /// 从内存获取服务凭证
1075    async fn get_credential(
1076        &self,
1077        service_id: &str,
1078    ) -> Result<Option<ServiceCredential>, SaTokenError> {
1079        let creds = self.credentials.read().await;
1080        Ok(creds.get(service_id).cloned())
1081    }
1082}
1083
1084/// Persist distributed sessions through SaTokenDao (same serializer and keys as login).
1085/// 经 SaTokenDao 持久化分布式会话(与登录共用序列化器和键)。
1086pub struct SaStorageDistributedStorage {
1087    dao: Arc<SaTokenDao>,
1088}
1089
1090impl std::fmt::Debug for SaStorageDistributedStorage {
1091    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
1092        f.write_str("SaStorageDistributedStorage { .. }")
1093    }
1094}
1095
1096impl SaStorageDistributedStorage {
1097    /// `from_dao` — from dao | `from_dao`
1098    pub fn from_dao(dao: Arc<SaTokenDao>) -> Self {
1099        Self { dao }
1100    }
1101
1102    /// Compatibility wrapper: builds a Dao from raw storage + prefix.
1103    /// 兼容包装:从原始存储与前缀构造 Dao。
1104    pub fn from_config(storage: Arc<dyn SaStorage>, config: &SaTokenConfig) -> Self {
1105        Self::from_dao(Arc::new(SaTokenDao::new(storage, Arc::new(config.clone()))))
1106    }
1107
1108    /// Create a new instance | 创建新实例
1109    pub fn new(storage: Arc<dyn SaStorage>, key_prefix: impl Into<String>) -> Self {
1110        let config = SaTokenConfig {
1111            storage_key_prefix: key_prefix.into(),
1112            ..SaTokenConfig::default()
1113        };
1114        Self::from_config(storage, &config)
1115    }
1116}
1117
1118#[async_trait]
1119impl DistributedSessionStorage for SaStorageDistributedStorage {
1120    async fn save_session(
1121        &self,
1122        session: DistributedSession,
1123        ttl: Option<Duration>,
1124    ) -> Result<(), SaTokenError> {
1125        let session_key = self.dao.keys().distributed_session(&session.session_id);
1126        let index_key = self.dao.keys().distributed_session_index(&session.login_id);
1127        self.dao.set_object(&session_key, &session, ttl).await?;
1128        self.dao
1129            .list_push_unique(&index_key, &session.session_id, None)
1130            .await?;
1131        Ok(())
1132    }
1133
1134    async fn get_session(
1135        &self,
1136        session_id: &str,
1137    ) -> Result<Option<DistributedSession>, SaTokenError> {
1138        self.dao
1139            .get_object(&self.dao.keys().distributed_session(session_id))
1140            .await
1141    }
1142
1143    async fn delete_session(&self, session_id: &str) -> Result<(), SaTokenError> {
1144        if let Some(session) = self.get_session(session_id).await? {
1145            self.dao
1146                .delete(&self.dao.keys().distributed_session(session_id))
1147                .await?;
1148            let index_key = self.dao.keys().distributed_session_index(&session.login_id);
1149            self.dao.list_remove(&index_key, session_id).await?;
1150            if self.dao.list_len(&index_key).await? == 0 {
1151                self.dao.delete(&index_key).await?;
1152            }
1153        } else {
1154            self.dao
1155                .delete(&self.dao.keys().distributed_session(session_id))
1156                .await?;
1157        }
1158        Ok(())
1159    }
1160
1161    async fn get_sessions_by_login_id(
1162        &self,
1163        login_id: &str,
1164    ) -> Result<Vec<DistributedSession>, SaTokenError> {
1165        let index_key = self.dao.keys().distributed_session_index(login_id);
1166        let ids = self.dao.list_range(&index_key, 0, None).await?;
1167        let mut out = Vec::new();
1168        for id in ids {
1169            match self.get_session(&id).await? {
1170                Some(s) => out.push(s),
1171                None => {
1172                    self.dao.list_remove(&index_key, &id).await?;
1173                }
1174            }
1175        }
1176        Ok(out)
1177    }
1178
1179    async fn save_credential(&self, credential: ServiceCredential) -> Result<(), SaTokenError> {
1180        let key = self.dao.keys().distributed_service(&credential.service_id);
1181        self.dao.set_object(&key, &credential, None).await
1182    }
1183
1184    async fn get_credential(
1185        &self,
1186        service_id: &str,
1187    ) -> Result<Option<ServiceCredential>, SaTokenError> {
1188        self.dao
1189            .get_object(&self.dao.keys().distributed_service(service_id))
1190            .await
1191    }
1192}
1193
1194#[cfg(test)]
1195mod tests {
1196    use super::*;
1197
1198    #[tokio::test]
1199    async fn test_distributed_session_manager() {
1200        let storage = Arc::new(InMemoryDistributedStorage::new());
1201        let manager = DistributedSessionManager::new(
1202            storage,
1203            "service1".to_string(),
1204            Duration::from_secs(3600),
1205        );
1206
1207        let session = manager
1208            .create_session("user1".to_string(), "token1".to_string())
1209            .await
1210            .unwrap();
1211
1212        let retrieved = manager.get_session(&session.session_id).await.unwrap();
1213        assert_eq!(retrieved.login_id, "user1");
1214    }
1215
1216    #[tokio::test]
1217    async fn test_session_attributes() {
1218        let storage = Arc::new(InMemoryDistributedStorage::new());
1219        let manager = DistributedSessionManager::new(
1220            storage,
1221            "service1".to_string(),
1222            Duration::from_secs(3600),
1223        );
1224
1225        let session = manager
1226            .create_session("user2".to_string(), "token2".to_string())
1227            .await
1228            .unwrap();
1229
1230        manager
1231            .set_attribute(
1232                &session.session_id,
1233                "key1".to_string(),
1234                "value1".to_string(),
1235            )
1236            .await
1237            .unwrap();
1238
1239        let value = manager
1240            .get_attribute(&session.session_id, "key1")
1241            .await
1242            .unwrap();
1243        assert_eq!(value, Some("value1".to_string()));
1244    }
1245
1246    #[tokio::test]
1247    async fn test_service_verification() {
1248        let storage = Arc::new(InMemoryDistributedStorage::new());
1249        let manager = DistributedSessionManager::new(
1250            storage,
1251            "service1".to_string(),
1252            Duration::from_secs(3600),
1253        );
1254
1255        let credential = ServiceCredential {
1256            service_id: "service2".to_string(),
1257            service_name: "Service 2".to_string(),
1258            secret_key: "secret123".to_string(),
1259            created_at: Utc::now(),
1260            permissions: vec!["read".to_string(), "write".to_string()],
1261        };
1262
1263        manager.register_service(credential.clone()).await.unwrap();
1264
1265        let verified = manager
1266            .verify_service("service2", "secret123")
1267            .await
1268            .unwrap();
1269        assert_eq!(verified.service_id, "service2");
1270
1271        let result = manager.verify_service("service2", "wrong_secret").await;
1272        assert!(result.is_err());
1273    }
1274
1275    #[tokio::test]
1276    async fn test_delete_all_sessions() {
1277        let storage = Arc::new(InMemoryDistributedStorage::new());
1278        let manager = DistributedSessionManager::new(
1279            storage,
1280            "service1".to_string(),
1281            Duration::from_secs(3600),
1282        );
1283
1284        manager
1285            .create_session("user3".to_string(), "token1".to_string())
1286            .await
1287            .unwrap();
1288        manager
1289            .create_session("user3".to_string(), "token2".to_string())
1290            .await
1291            .unwrap();
1292
1293        let sessions = manager.get_sessions_by_login_id("user3").await.unwrap();
1294        assert_eq!(sessions.len(), 2);
1295
1296        manager.delete_all_sessions("user3").await.unwrap();
1297
1298        let sessions = manager.get_sessions_by_login_id("user3").await.unwrap();
1299        assert_eq!(sessions.len(), 0);
1300    }
1301}