Skip to main content

sa_token_core/
stp_logic.rs

1// Author: 金书记
2//
3//! 多账号体系门面:绑定 login_type + Manager 克隆(内部字段均为 Arc,Clone 廉价)。
4//! Multi-account facade: binds login_type + a cloned Manager (fields are Arc; Clone is cheap).
5//! 无进程级 HashMap,避免与 Manager 形成引用环。
6//! No process-wide HashMap, avoiding a reference cycle with Manager.
7
8use std::sync::Arc;
9
10use crate::disable;
11use crate::error::SaTokenResult;
12use crate::keys::SaKeys;
13use crate::manager::SaTokenManager;
14use crate::session::{SaSession, SaTerminalInfo};
15use crate::token::TokenValue;
16
17/// 绑定某一 login_type 的账号逻辑门面
18/// Account-logic facade bound to one login_type
19#[derive(Clone)]
20pub struct SaLogic {
21    login_type: Arc<str>,
22    manager: SaTokenManager,
23}
24
25impl std::fmt::Debug for SaLogic {
26    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
27        f.write_str("SaLogic { .. }")
28    }
29}
30
31impl SaLogic {
32    /// 创建门面(廉价 Clone)
33    /// Create facade (cheap to Clone)
34    pub fn new(login_type: impl AsRef<str>, manager: SaTokenManager) -> Self {
35        Self {
36            login_type: Arc::from(login_type.as_ref()),
37            manager,
38        }
39    }
40
41    /// Account system / login type | 账号体系/登录类型
42    pub fn login_type(&self) -> &str {
43        &self.login_type
44    }
45
46    /// Underlying manager | 底层管理器
47    pub fn manager(&self) -> &SaTokenManager {
48        &self.manager
49    }
50
51    /// Key layout helper | 键布局辅助
52    pub fn keys(&self) -> &SaKeys {
53        self.manager.keys()
54    }
55
56    /// Login and issue a token | 登录并签发 Token
57    pub async fn login(&self, login_id: impl Into<String>) -> SaTokenResult<TokenValue> {
58        self.manager
59            .login_with_options(
60                login_id,
61                Some(self.login_type.to_string()),
62                None,
63                None,
64                None,
65                None,
66            )
67            .await
68    }
69
70    /// Login with device label | 带设备标识登录
71    pub async fn login_with_device(
72        &self,
73        login_id: impl Into<String>,
74        device: Option<String>,
75        extra: Option<serde_json::Value>,
76    ) -> SaTokenResult<TokenValue> {
77        self.manager
78            .login_with_options(
79                login_id,
80                Some(self.login_type.to_string()),
81                device,
82                extra,
83                None,
84                None,
85            )
86            .await
87    }
88
89    /// Logout current token | 登出当前 Token
90    pub async fn logout(&self, token: &TokenValue) -> SaTokenResult<()> {
91        self.manager.logout(token).await
92    }
93
94    /// Logout all tokens of a login id | 登出某登录 ID 的全部 Token
95    pub async fn logout_by_login_id(&self, login_id: &str) -> SaTokenResult<()> {
96        self.manager
97            .logout_by_login_id(&self.login_type, login_id)
98            .await
99    }
100
101    /// Kick out and optionally notify | 踢下线并可通知
102    pub async fn kick_out(&self, login_id: &str) -> SaTokenResult<()> {
103        self.manager.kick_out(&self.login_type, login_id).await
104    }
105
106    /// Resolve login id from token | 从 Token 解析登录 ID
107    pub async fn get_login_id(&self, token: &TokenValue) -> SaTokenResult<String> {
108        Ok(self
109            .manager
110            .get_token_info(token)
111            .await?
112            .login_id
113            .to_string())
114    }
115
116    /// Whether the token is valid | Token 是否有效
117    pub async fn is_valid(&self, token: &TokenValue) -> bool {
118        self.manager.is_valid(token).await
119    }
120
121    /// Load account session | 加载账号 Session
122    pub async fn get_session(&self, login_id: &str) -> SaTokenResult<SaSession> {
123        self.manager
124            .get_session_with_type(&self.login_type, login_id)
125            .await
126    }
127
128    /// Persist account session | 持久化账号 Session
129    pub async fn save_session(&self, login_id: &str, session: &SaSession) -> SaTokenResult<()> {
130        self.manager
131            .save_session_with_type(&self.login_type, login_id, session)
132            .await
133    }
134
135    /// Delete account session | 删除账号 Session
136    pub async fn delete_session(&self, login_id: &str) -> SaTokenResult<()> {
137        self.manager
138            .delete_session_with_type(&self.login_type, login_id)
139            .await
140    }
141
142    /// List terminals for the account | 列出账号终端
143    pub async fn get_terminal_list(
144        &self,
145        login_id: &str,
146        device_type: Option<&str>,
147    ) -> SaTokenResult<Vec<SaTerminalInfo>> {
148        self.manager
149            .get_terminal_list(&self.login_type, login_id, device_type)
150            .await
151    }
152
153    /// Terminal info for a token | 按 Token 查终端信息
154    pub async fn get_terminal_info_by_token(
155        &self,
156        token: &TokenValue,
157    ) -> SaTokenResult<Option<SaTerminalInfo>> {
158        self.manager.get_terminal_info_by_token(token).await
159    }
160
161    // ---------- 权限 | Permissions ----------
162
163    /// 获取权限列表 | Permission list
164    pub async fn get_permissions(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
165        self.manager
166            .get_permissions_with_type(&self.login_type, login_id)
167            .await
168    }
169
170    /// 覆盖权限列表 | Overwrite the permission list
171    pub async fn set_permissions(&self, login_id: &str, perms: Vec<String>) -> SaTokenResult<()> {
172        self.manager
173            .set_permissions_with_type(&self.login_type, login_id, perms)
174            .await
175    }
176
177    /// 追加单个权限(B2-35 新增)| Append one permission (new)
178    pub async fn add_permission(
179        &self,
180        login_id: &str,
181        permission: impl Into<String>,
182    ) -> SaTokenResult<()> {
183        self.manager
184            .add_permission_with_type(&self.login_type, login_id, permission.into())
185            .await
186    }
187
188    /// 移除单个权限(B2-35 新增)| Remove one permission (new)
189    pub async fn remove_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<()> {
190        self.manager
191            .remove_permission_with_type(&self.login_type, login_id, permission)
192            .await
193    }
194
195    /// 清空权限(B2-35 新增)| Clear permissions (new)
196    pub async fn clear_permissions(&self, login_id: &str) -> SaTokenResult<()> {
197        self.manager
198            .clear_permissions_with_type(&self.login_type, login_id)
199            .await
200    }
201
202    /// 单个权限校验(B2-27 新增)| Single permission check (new)
203    pub async fn has_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<bool> {
204        self.manager
205            .authz_service()
206            .has_permission(&self.login_type, login_id, permission)
207            .await
208    }
209
210    /// 权限校验,不足则返回 `Err`(B2-27 新增)| Permission check returning `Err` (new)
211    pub async fn check_permission(&self, login_id: &str, permission: &str) -> SaTokenResult<()> {
212        self.manager
213            .authz_service()
214            .check_permission(&self.login_type, login_id, permission)
215            .await
216    }
217
218    /// 批量权限校验(AND,B2-27 新增)| Batch AND permission check (new)
219    pub async fn has_all_permissions(
220        &self,
221        login_id: &str,
222        permissions: &[&str],
223    ) -> SaTokenResult<bool> {
224        self.manager
225            .authz_service()
226            .has_all_permissions(&self.login_type, login_id, permissions)
227            .await
228    }
229
230    /// 批量权限校验(OR,B2-27 新增)| Batch OR permission check (new)
231    pub async fn has_any_permission(
232        &self,
233        login_id: &str,
234        permissions: &[&str],
235    ) -> SaTokenResult<bool> {
236        self.manager
237            .authz_service()
238            .has_any_permission(&self.login_type, login_id, permissions)
239            .await
240    }
241
242    // ---------- 角色 | Roles ----------
243
244    /// 获取角色列表 | Role list
245    pub async fn get_roles(&self, login_id: &str) -> SaTokenResult<Vec<String>> {
246        self.manager
247            .get_roles_with_type(&self.login_type, login_id)
248            .await
249    }
250
251    /// 覆盖角色列表 | Overwrite the role list
252    pub async fn set_roles(&self, login_id: &str, roles: Vec<String>) -> SaTokenResult<()> {
253        self.manager
254            .set_roles_with_type(&self.login_type, login_id, roles)
255            .await
256    }
257
258    /// 追加单个角色(B2-35 新增)| Append one role (new)
259    pub async fn add_role(&self, login_id: &str, role: impl Into<String>) -> SaTokenResult<()> {
260        self.manager
261            .add_role_with_type(&self.login_type, login_id, role.into())
262            .await
263    }
264
265    /// 移除单个角色(B2-35 新增)| Remove one role (new)
266    pub async fn remove_role(&self, login_id: &str, role: &str) -> SaTokenResult<()> {
267        self.manager
268            .remove_role_with_type(&self.login_type, login_id, role)
269            .await
270    }
271
272    /// 清空角色(B2-35 新增)| Clear roles (new)
273    pub async fn clear_roles(&self, login_id: &str) -> SaTokenResult<()> {
274        self.manager
275            .clear_roles_with_type(&self.login_type, login_id)
276            .await
277    }
278
279    /// 单个角色校验(B2-27 新增)| Single role check (new)
280    pub async fn has_role(&self, login_id: &str, role: &str) -> SaTokenResult<bool> {
281        self.manager
282            .authz_service()
283            .has_role(&self.login_type, login_id, role)
284            .await
285    }
286
287    /// 角色校验,不足则返回 `Err`(B2-27 新增)| Role check returning `Err` (new)
288    pub async fn check_role(&self, login_id: &str, role: &str) -> SaTokenResult<()> {
289        self.manager
290            .authz_service()
291            .check_role(&self.login_type, login_id, role)
292            .await
293    }
294
295    /// 批量角色校验(AND,B2-27 新增)| Batch AND role check (new)
296    pub async fn has_all_roles(&self, login_id: &str, roles: &[&str]) -> SaTokenResult<bool> {
297        self.manager
298            .authz_service()
299            .has_all_roles(&self.login_type, login_id, roles)
300            .await
301    }
302
303    /// 批量角色校验(OR,B2-27 新增)| Batch OR role check (new)
304    pub async fn has_any_role(&self, login_id: &str, roles: &[&str]) -> SaTokenResult<bool> {
305        self.manager
306            .authz_service()
307            .has_any_role(&self.login_type, login_id, roles)
308            .await
309    }
310
311    // ---------- 封禁 | Disable ----------
312
313    /// Disable account/service | 禁用账号或服务
314    pub async fn disable(&self, login_id: &str, time: i64) -> SaTokenResult<()> {
315        self.manager
316            .disable_with_type(&self.login_type, login_id, time)
317            .await
318    }
319
320    /// Disable at a level (default login type) | 分级禁用(默认登录类型)
321    pub async fn disable_level(
322        &self,
323        login_id: &str,
324        service: &str,
325        level: i32,
326        time: i64,
327    ) -> SaTokenResult<()> {
328        self.manager
329            .disable_level_with_type(&self.login_type, login_id, service, level, time)
330            .await
331    }
332
333    /// Fail if account is disabled | 账号被禁用则报错
334    pub async fn check_disable(&self, login_id: &str) -> SaTokenResult<()> {
335        self.manager
336            .check_disable_level_with_type(
337                &self.login_type,
338                login_id,
339                disable::DEFAULT_DISABLE_SERVICE,
340                disable::MIN_DISABLE_LEVEL,
341            )
342            .await
343    }
344
345    /// Read disable level | 读取禁用等级
346    pub async fn get_disable_level(&self, login_id: &str, service: &str) -> SaTokenResult<i32> {
347        self.manager
348            .get_disable_level_with_type(&self.login_type, login_id, service)
349            .await
350    }
351
352    /// Clear disable flag | 解除禁用
353    pub async fn untie_disable(&self, login_id: &str, service: &str) -> SaTokenResult<()> {
354        self.manager
355            .untie_disable_with_type(&self.login_type, login_id, service)
356            .await
357    }
358
359    // ---------- 二级认证 | Safe auth ----------
360
361    /// Open secondary auth window | 开启二级认证窗口
362    pub async fn open_safe(
363        &self,
364        token: &TokenValue,
365        service: &str,
366        safe_time: i64,
367    ) -> SaTokenResult<()> {
368        self.manager.open_safe(token, service, safe_time).await
369    }
370
371    /// Fail if secondary auth missing | 未通过二级认证则报错
372    pub async fn check_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<()> {
373        self.manager.check_safe(token, service).await
374    }
375
376    /// Whether secondary auth is active | 二级认证是否有效
377    pub async fn is_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<bool> {
378        self.manager.is_safe(token, service).await
379    }
380
381    /// Close secondary auth window | 关闭二级认证窗口
382    pub async fn close_safe(&self, token: &TokenValue, service: &str) -> SaTokenResult<()> {
383        self.manager.close_safe(token, service).await
384    }
385
386    // ---------- Token Session ----------
387
388    /// Load token-scoped session | 加载 Token 级 Session
389    pub async fn get_token_session(&self, token: &TokenValue) -> SaTokenResult<SaSession> {
390        self.manager.get_token_session(token).await
391    }
392
393    /// Load anonymous token session | 加载匿名 Token Session
394    pub async fn get_anon_token_session(&self, token: &TokenValue) -> SaTokenResult<SaSession> {
395        self.manager.get_anon_token_session(token).await
396    }
397
398    /// `save_token_session` — save token session | `save_token_session`
399    pub async fn save_token_session(
400        &self,
401        token: &TokenValue,
402        session: &SaSession,
403    ) -> SaTokenResult<()> {
404        self.manager.save_token_session(token, session).await
405    }
406
407    /// `delete_token_session` — delete token session | `delete_token_session`
408    pub async fn delete_token_session(&self, token: &TokenValue) -> SaTokenResult<()> {
409        self.manager.delete_token_session(token).await
410    }
411
412    // ---------- 身份临时切换 | Identity switch ----------
413
414    /// 必须走 B3 单轨突变,否则 task-local 路径静默失效。
415    /// Must use B3 single-track mutation or task-local switch silently fails.
416    pub fn switch_to(&self, login_id: impl Into<String>) {
417        let target = login_id.into();
418        crate::context::SaTokenContext::with_current_mut(|inner| {
419            inner.switch_login_id = Some(target);
420        });
421    }
422
423    /// End identity switch | 结束身份切换
424    pub fn end_switch(&self) {
425        crate::context::SaTokenContext::with_current_mut(|inner| {
426            inner.switch_login_id = None;
427        });
428    }
429
430    /// Whether identity is switched | 是否处于身份切换中
431    pub fn is_switch(&self) -> bool {
432        crate::context::SaTokenContext::get_current()
433            .and_then(|c| c.switch_login_id())
434            .is_some()
435    }
436}
437
438#[cfg(test)]
439mod tests {
440    use super::*;
441    use sa_token_storage_memory::MemoryStorage;
442
443    fn make_manager() -> SaTokenManager {
444        SaTokenManager::new(
445            Arc::new(MemoryStorage::new()),
446            crate::SaTokenConfig::default(),
447        )
448    }
449
450    #[tokio::test]
451    async fn test_sa_logic_permission_isolation() {
452        let mgr = make_manager();
453        let admin = SaLogic::new("admin", mgr.clone());
454        let user = SaLogic::new("user", mgr);
455
456        admin
457            .set_permissions("10001", vec!["admin:read".to_string()])
458            .await
459            .unwrap();
460        user.set_permissions("10001", vec!["user:read".to_string()])
461            .await
462            .unwrap();
463
464        assert_eq!(
465            admin.get_permissions("10001").await.unwrap(),
466            vec!["admin:read".to_string()]
467        );
468        assert_eq!(
469            user.get_permissions("10001").await.unwrap(),
470            vec!["user:read".to_string()]
471        );
472    }
473
474    #[tokio::test]
475    async fn test_sa_logic_clone_is_independent_facade() {
476        let mgr = make_manager();
477        let a = SaLogic::new("shared", mgr.clone());
478        let b = SaLogic::new("shared", mgr);
479        assert_eq!(a.login_type(), b.login_type());
480        assert_eq!(a.login_type(), "shared");
481    }
482}