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}