sa_token_core/
router.rs

1// Author: 金书记
2//
3// Path-based authentication router module
4// 基于路径的鉴权路由模块
5
6use std::sync::Arc;
7
8/// Match a path against a pattern (Ant-style wildcard)
9/// 匹配路径与模式(Ant 风格通配符)
10///
11/// # Arguments
12/// - `path`: The request path to match
13/// - `pattern`: The pattern to match against
14///
15/// # Patterns Supported
16/// - `/**`: Match all paths
17/// - `/api/**`: Match all paths starting with `/api/`
18/// - `/api/*`: Match single-level paths under `/api/`
19/// - `*.html`: Match paths ending with `.html`
20/// - `/exact`: Exact match
21///
22/// # Examples
23/// ```
24/// use sa_token_core::router::match_path;
25/// assert!(match_path("/api/user", "/api/**"));
26/// assert!(match_path("/api/user", "/api/*"));
27/// assert!(!match_path("/api/user/profile", "/api/*"));
28/// ```
29pub fn match_path(path: &str, pattern: &str) -> bool {
30    if pattern == "/**" {
31        return true;
32    }
33    if pattern.ends_with("/**") {
34        let prefix = &pattern[..pattern.len() - 3];
35        return path.starts_with(prefix);
36    }
37    if pattern.starts_with("*") {
38        let suffix = &pattern[1..];
39        return path.ends_with(suffix);
40    }
41    if pattern.ends_with("/*") {
42        let prefix = &pattern[..pattern.len() - 2];
43        if path.starts_with(prefix) {
44            let suffix = &path[prefix.len()..];
45            return !suffix.contains('/') || suffix == "/";
46        }
47        return false;
48    }
49    path == pattern
50}
51
52/// Check if path matches any pattern in the list
53/// 检查路径是否匹配列表中的任意模式
54pub fn match_any(path: &str, patterns: &[&str]) -> bool {
55    patterns.iter().any(|p| match_path(path, p))
56}
57
58/// Determine if authentication is needed for a path
59/// 判断路径是否需要鉴权
60///
61/// Returns `true` if path matches include patterns but not exclude patterns
62/// 如果路径匹配包含模式但不匹配排除模式,返回 `true`
63pub fn need_auth(path: &str, include: &[&str], exclude: &[&str]) -> bool {
64    match_any(path, include) && !match_any(path, exclude)
65}
66
67/// Path-based authentication configuration
68/// 基于路径的鉴权配置
69///
70/// Configure which paths require authentication and which are excluded
71/// 配置哪些路径需要鉴权,哪些路径被排除
72#[derive(Clone)]
73pub struct PathAuthConfig {
74    /// Paths that require authentication (include patterns)
75    /// 需要鉴权的路径(包含模式)
76    include: Vec<String>,
77    /// Paths excluded from authentication (exclude patterns)
78    /// 排除鉴权的路径(排除模式)
79    exclude: Vec<String>,
80    /// Optional login ID validator function
81    /// 可选的登录ID验证函数
82    validator: Option<Arc<dyn Fn(&str) -> bool + Send + Sync>>,
83}
84
85impl PathAuthConfig {
86    /// Create a new path authentication configuration
87    /// 创建新的路径鉴权配置
88    pub fn new() -> Self {
89        Self {
90            include: Vec::new(),
91            exclude: Vec::new(),
92            validator: None,
93        }
94    }
95
96    /// Set paths that require authentication
97    /// 设置需要鉴权的路径
98    pub fn include(mut self, patterns: Vec<String>) -> Self {
99        self.include = patterns;
100        self
101    }
102
103    /// Set paths excluded from authentication
104    /// 设置排除鉴权的路径
105    pub fn exclude(mut self, patterns: Vec<String>) -> Self {
106        self.exclude = patterns;
107        self
108    }
109
110    /// Set a custom login ID validator function
111    /// 设置自定义的登录ID验证函数
112    pub fn validator<F>(mut self, f: F) -> Self
113    where
114        F: Fn(&str) -> bool + Send + Sync + 'static,
115    {
116        self.validator = Some(Arc::new(f));
117        self
118    }
119
120    /// Check if a path requires authentication
121    /// 检查路径是否需要鉴权
122    pub fn check(&self, path: &str) -> bool {
123        let inc: Vec<&str> = self.include.iter().map(|s| s.as_str()).collect();
124        let exc: Vec<&str> = self.exclude.iter().map(|s| s.as_str()).collect();
125        need_auth(path, &inc, &exc)
126    }
127
128    /// Validate a login ID using the configured validator
129    /// 使用配置的验证器验证登录ID
130    pub fn validate_login_id(&self, login_id: &str) -> bool {
131        self.validator.as_ref().map_or(true, |v| v(login_id))
132    }
133}
134
135impl Default for PathAuthConfig {
136    fn default() -> Self {
137        Self::new()
138    }
139}
140
141use crate::{SaTokenManager, TokenValue, SaTokenContext, token::TokenInfo};
142
143/// Authentication result after processing
144/// 处理后的鉴权结果
145pub struct AuthResult {
146    /// Whether authentication is required for this path
147    /// 此路径是否需要鉴权
148    pub need_auth: bool,
149    /// Extracted token value
150    /// 提取的token值
151    pub token: Option<TokenValue>,
152    /// Token information if valid
153    /// 如果有效则包含token信息
154    pub token_info: Option<TokenInfo>,
155    /// Whether the token is valid
156    /// token是否有效
157    pub is_valid: bool,
158}
159
160impl AuthResult {
161    /// Check if the request should be rejected
162    /// 检查请求是否应该被拒绝
163    pub fn should_reject(&self) -> bool {
164        self.need_auth && (!self.is_valid || self.token.is_none())
165    }
166
167    /// Get the login ID from token info
168    /// 从token信息中获取登录ID
169    pub fn login_id(&self) -> Option<&str> {
170        self.token_info.as_ref().map(|t| t.login_id.as_str())
171    }
172}
173
174/// Process authentication for a request path
175/// 处理请求路径的鉴权
176///
177/// This function checks if the path requires authentication, validates the token,
178/// and returns an AuthResult with all relevant information.
179/// 此函数检查路径是否需要鉴权,验证token,并返回包含所有相关信息的AuthResult。
180///
181/// # Arguments
182/// - `path`: The request path
183/// - `token_str`: Optional token string from request
184/// - `config`: Path authentication configuration
185/// - `manager`: SaTokenManager instance
186pub async fn process_auth(
187    path: &str,
188    token_str: Option<String>,
189    config: &PathAuthConfig,
190    manager: &SaTokenManager,
191) -> AuthResult {
192    let need_auth = config.check(path);
193    
194    let token = token_str.map(TokenValue::new);
195    
196    let (is_valid, token_info) = if let Some(ref t) = token {
197        let valid = manager.is_valid(t).await;
198        let info = if valid {
199            manager.get_token_info(t).await.ok()
200        } else {
201            None
202        };
203        (valid, info)
204    } else {
205        (false, None)
206    };
207
208    let is_valid = is_valid && if need_auth {
209        token_info.as_ref().map_or(false, |info| config.validate_login_id(&info.login_id))
210    } else {
211        true
212    };
213
214    AuthResult {
215        need_auth,
216        token,
217        token_info,
218        is_valid,
219    }
220}
221
222/// Create SaTokenContext from authentication result
223/// 从鉴权结果创建SaTokenContext
224pub fn create_context(result: &AuthResult) -> SaTokenContext {
225    let mut ctx = SaTokenContext::new();
226    if let (Some(token), Some(info)) = (&result.token, &result.token_info) {
227        ctx.token = Some(token.clone());
228        ctx.token_info = Some(Arc::new(info.clone()));
229        ctx.login_id = Some(info.login_id.clone());
230    }
231    ctx
232}
233