Skip to content

Distributed Session Management

English

Overview

The Distributed Session Management module enables session sharing across multiple microservices. It provides service authentication, cross-service session access, and attribute management with automatic timeout handling.

This module is designed for microservices architectures where multiple services need to share user authentication state and session data seamlessly.

Architecture

text
┌────────────────────────────────────────────────────────────────────┐
│                   Microservices Architecture                       │
│                   微服务架构                                        │
└────────────────────────────────────────────────────────────────────┘

    ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
    │  Service A   │  │  Service B   │  │  Service C   │
    │  (User API)  │  │  (Order API) │  │  (Pay API)   │
    └──────┬───────┘  └──────┬───────┘  └──────┬───────┘
           │                  │                  │
           └──────────────────┼──────────────────┘

                    ┌─────────▼──────────┐
                    │  Distributed       │
                    │  Session Storage   │
                    │  (Redis/Database)  │
                    └────────────────────┘

Each service can:
  - Create sessions for users
  - Access sessions created by other services
  - Share user authentication state

Key Features

  • Cross-Service Session Sharing - Share sessions across microservices
  • Service Authentication - Verify service credentials with secret keys
  • Session Attributes - Store custom key-value pairs for user context
  • Multi-Session Support - One user can have multiple sessions (multi-device)
  • Automatic Cleanup - TTL-based session expiration
  • Pluggable Storage - Use custom storage backends (Redis, Database, Memory)
  • Permission-Based Access - Fine-grained control via service permissions
  • Session Monitoring - Track all active sessions per user

Quick Start

rust
use sa_token_core::{
    DistributedSessionManager, InMemoryDistributedStorage, ServiceCredential
};
use std::sync::Arc;
use std::time::Duration;
use chrono::Utc;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    // Create distributed session manager
    let storage = Arc::new(InMemoryDistributedStorage::new());
    let manager = DistributedSessionManager::new(
        storage,
        "service-main".to_string(),
        Duration::from_secs(3600), // 1 hour TTL
    );
    
    // Register a service
    let credential = ServiceCredential {
        service_id: "api-gateway".to_string(),
        service_name: "API Gateway".to_string(),
        secret_key: "secret123".to_string(),
        created_at: Utc::now(),
        permissions: vec!["read".to_string(), "write".to_string()],
    };
    manager.register_service(credential).await;
    
    // Verify service
    let verified = manager.verify_service("api-gateway", "secret123").await?;
    println!("Service verified: {}", verified.service_name);
    
    // Create session
    let session = manager.create_session(
        "user123".to_string(),
        "token456".to_string(),
    ).await?;
    
    // Set session attribute
    manager.set_attribute(
        &session.session_id,
        "role".to_string(),
        "admin".to_string(),
    ).await?;
    
    // Get session attribute
    if let Some(role) = manager.get_attribute(&session.session_id, "role").await? {
        println!("User role: {}", role);
    }
    
    // Get all sessions for user
    let sessions = manager.get_sessions_by_login_id("user123").await?;
    println!("User has {} active sessions", sessions.len());
    
    Ok(())
}

Service Authentication Flow

text
Service A                   Manager                    Service B
   |                           |                           |
   |-- register_service ------>|                           |
   |<----- registered ---------|                           |
   |                           |                           |
   |                           |<-- verify_service(id, secret)
   |                           |--- check credentials ---->|
   |                           |<----- verified ----------|

Cross-Service Session Access

text
Service A creates session:
  session_id: "uuid-123"
  login_id: "user123"
  attributes: {"role": "admin"}

Service B accesses session:
  get_session("uuid-123") -> Full session data
  Can read/modify attributes
  Updates last_access timestamp

API Reference

DistributedSessionManager

Methods:

  • new(storage, service_id, timeout) - Create manager
  • register_service(credential) - Register a service
  • verify_service(id, secret) - Verify service credentials
  • create_session(login_id, token) - Create new session
  • get_session(session_id) - Get session by ID
  • update_session(session) - Update existing session
  • delete_session(session_id) - Delete session
  • set_attribute(id, key, value) - Set session attribute
  • get_attribute(id, key) - Get session attribute
  • remove_attribute(id, key) - Remove session attribute
  • get_sessions_by_login_id(login_id) - Get all user sessions
  • delete_all_sessions(login_id) - Delete all user sessions

Use Cases

1. Single Sign-On (SSO) Across Services

Users log in once and access multiple services without re-authentication:

text
User → Service A: Login
  ├─ Create session: session_id = "abc123"
  └─ Save to distributed storage

User → Service B: Request with session_id = "abc123"
  ├─ Service B retrieves session from storage
  ├─ Validates user is authenticated
  └─ Processes request ✅ (No re-login needed!)

2. Session Sharing for User Context

Services share user context and state:

text
Service A stores: { "user_role": "admin", "department": "IT" }
Service B reads: Same session attributes available
Service C updates: { "last_order": "order_123" }
→ All services share the same session state!

3. Multi-Device Session Management

One user can have multiple active sessions:

text
User: user_123
  ├─ Session 1: Web (Service A)
  ├─ Session 2: Mobile (Service B)
  └─ Session 3: Desktop (Service C)

All sessions can be:
  - Listed: get_sessions_by_login_id()
  - Managed individually
  - Terminated all at once: delete_all_sessions()

4. Microservices Architecture

Share user sessions across API Gateway, User Service, Order Service, etc.

5. Multi-Region Deployment

Synchronize sessions across different geographic regions using shared storage.

6. Load Balancing

Maintain session consistency across multiple server instances.

Storage Backends

rust
use redis::AsyncCommands;

pub struct RedisDistributedStorage {
    client: redis::Client,
}

#[async_trait]
impl DistributedSessionStorage for RedisDistributedStorage {
    async fn save_session(&self, session: DistributedSession, ttl: Option<Duration>) 
        -> Result<(), SaTokenError> 
    {
        let mut conn = self.client.get_async_connection().await?;
        let key = format!("distributed:session:{}", session.session_id);
        let value = serde_json::to_string(&session)?;
        
        if let Some(ttl) = ttl {
            conn.set_ex(&key, value, ttl.as_secs() as usize).await?;
        } else {
            conn.set(&key, value).await?;
        }
        
        // Index by login_id for quick lookup
        let index_key = format!("distributed:login:{}", session.login_id);
        conn.sadd(index_key, &session.session_id).await?;
        
        Ok(())
    }
    
    // ... implement other methods
}

Database Implementation

rust
use sqlx::PgPool;

pub struct PostgresDistributedStorage {
    pool: PgPool,
}

#[async_trait]
impl DistributedSessionStorage for PostgresDistributedStorage {
    async fn save_session(&self, session: DistributedSession, ttl: Option<Duration>) 
        -> Result<(), SaTokenError> 
    {
        let expires_at = ttl.map(|t| Utc::now() + chrono::Duration::from_std(t).unwrap());
        
        sqlx::query!(
            "INSERT INTO distributed_sessions 
             (session_id, login_id, token, service_id, attributes, expires_at)
             VALUES ($1, $2, $3, $4, $5, $6)
             ON CONFLICT (session_id) DO UPDATE 
             SET attributes = $5, last_access = NOW()",
            session.session_id,
            session.login_id,
            session.token,
            session.service_id,
            serde_json::to_value(&session.attributes)?,
            expires_at,
        )
        .execute(&self.pool)
        .await?;
        
        Ok(())
    }
    
    // ... implement other methods
}

Best Practices

1. Service Registration

Use crypto-secure secret generation for service credentials:

rust
let credential = ServiceCredential {
    service_id: "user-service".to_string(),
    service_name: "User Management Service".to_string(),
    secret_key: generate_secure_secret(), // Use crypto-secure generation
    created_at: Utc::now(),
    permissions: vec!["user.read".to_string(), "user.write".to_string()],
};
manager.register_service(credential).await;

2. Session Creation with Context

Add relevant attributes immediately after session creation:

rust
let session = manager.create_session(login_id, token).await?;

// Add relevant attributes immediately
manager.set_attribute(&session.session_id, "user_role".to_string(), "admin".to_string()).await?;
manager.set_attribute(&session.session_id, "department".to_string(), "IT".to_string()).await?;
manager.set_attribute(&session.session_id, "login_device".to_string(), "web".to_string()).await?;

3. Cross-Service Access Pattern

Always verify service identity and check permissions:

rust
// 1. Verify service identity
let service_cred = manager.verify_service("service-b", request.secret).await?;

// 2. Check permissions
if !service_cred.permissions.contains(&"session.read".to_string()) {
    return Err(SaTokenError::PermissionDenied);
}

// 3. Access session
let session = manager.get_session(&request.session_id).await?;

// 4. Refresh to keep session alive
manager.refresh_session(&session.session_id).await?;

4. Multi-Device Logout

Support both individual and bulk logout:

rust
// Logout from all devices
manager.delete_all_sessions(&login_id).await?;

// Or logout specific session
manager.delete_session(&session_id).await?;

5. Session Monitoring

Monitor user's active sessions for security:

rust
let sessions = manager.get_sessions_by_login_id(&login_id).await?;

for session in sessions {
    println!("Session: {} from service: {}, last active: {}", 
        session.session_id,
        session.service_id,
        session.last_access
    );
    
    // Check for suspicious activity
    if is_suspicious(&session) {
        manager.delete_session(&session.session_id).await?;
    }
}

6. Security Considerations

  • Service Authentication: Each service has unique secret_key
  • Permission-Based Access: Services have explicit permissions
  • Session Timeout: Configure appropriate TTL
  • Data Encryption: Encrypt sensitive session attributes
  • Audit Logging: Log session creation/deletion and cross-service access

7. Production Recommendations

  1. Use appropriate TTL - Set session timeout based on security requirements (typically 1-24 hours)
  2. Use persistent storage - Implement Redis/Database storage for production (not in-memory)
  3. Secure service credentials - Use strong secret keys and rotate periodically
  4. Monitor session count - Track active sessions per user to detect anomalies
  5. Implement cleanup - Use storage TTL features for automatic cleanup
  6. Enable encryption - Encrypt sensitive session attributes at rest

License

MIT OR Apache-2.0

Released under the Apache-2.0 / MIT License.