Skip to main content

sa_token_core/sso/
session_store.rs

1// Author: 金书记 | Author: Jin Shuji
2//! SSO session persistence via SaTokenDao.
3//! 经 SaTokenDao 持久化 SSO 会话。
4
5use std::sync::Arc;
6
7use crate::dao::SaTokenDao;
8use crate::error::{SaTokenError, SaTokenResult};
9use crate::sso::SsoSession;
10
11/// Session store with CAS retry on client upsert.
12/// 带 CAS 重试的会话存储(客户端 upsert)。
13pub struct SsoSessionStore {
14    dao: Arc<SaTokenDao>,
15}
16
17impl std::fmt::Debug for SsoSessionStore {
18    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
19        f.write_str("SsoSessionStore { .. }")
20    }
21}
22
23impl SsoSessionStore {
24    /// Create a session store.
25    /// 创建会话存储。
26    pub fn new(dao: Arc<SaTokenDao>) -> Self {
27        Self { dao }
28    }
29
30    /// Add a client URL to the session with CAS retries (max 8).
31    /// 以 CAS 重试(最多 8 次)将会话加入客户端 URL。
32    pub async fn upsert_client(&self, login_id: &str, service: &str) -> SaTokenResult<()> {
33        let key = self.dao.keys().sso_session(login_id);
34        for _ in 0..8 {
35            let current = self.dao.get_string(&key).await?;
36            let mut session = match &current {
37                Some(raw) => self.dao.decode::<SsoSession>(raw)?,
38                None => SsoSession::new(login_id.to_string()),
39            };
40            session.add_client(service.to_string());
41            let new_raw = self.dao.encode(&session)?;
42            let ok = self
43                .dao
44                .cas(&key, current.as_deref(), &new_raw, None)
45                .await?;
46            if ok {
47                return Ok(());
48            }
49        }
50        Err(SaTokenError::InternalError(
51            "SSO session CAS retries exhausted".into(),
52        ))
53    }
54
55    /// Remove session and return tracked client URLs.
56    /// 删除会话并返回已跟踪的客户端 URL。
57    pub async fn remove(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
58        let key = self.dao.keys().sso_session(login_id);
59        let session = self.dao.get_object::<SsoSession>(&key).await?;
60        let clients = session.map(|s| s.clients).unwrap_or_default();
61        self.dao.delete(&key).await?;
62        Ok(clients)
63    }
64
65    /// Load session if present.
66    /// 若存在则加载会话。
67    pub async fn get(&self, login_id: &str) -> SaTokenResult<Option<SsoSession>> {
68        let key = self.dao.keys().sso_session(login_id);
69        self.dao.get_object(&key).await
70    }
71}