spring_sa_token/
configurator.rs

1//! Sa-Token Configurator module
2//!
3//! This module provides a Spring Security-like configuration mechanism
4//! for path-based authentication.
5//!
6//! # Example
7//!
8//! Define your security configuration in a separate file:
9//!
10//! ```rust,ignore
11//! // security.rs
12//! use spring_sa_token::{PathAuthBuilder, SaTokenConfigurator};
13//!
14//! pub struct SecurityConfig;
15//!
16//! impl SaTokenConfigurator for SecurityConfig {
17//!     fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
18//!         auth.include("/user/**")
19//!             .include("/admin/**")
20//!             .exclude("/login")
21//!             .exclude("/public/**")
22//!     }
23//! }
24//! ```
25//!
26//! Then use it in main.rs:
27//!
28//! ```rust,ignore
29//! // main.rs
30//! mod security;
31//! use spring_sa_token::SaTokenAuthConfigurator;
32//!
33//! App::new()
34//!     .add_plugin(RedisPlugin)
35//!     .add_plugin(SaTokenPlugin)
36//!     .add_plugin(WebPlugin)
37//!     .sa_token_auth(security::SecurityConfig)
38//!     .run()
39//!     .await
40//! ```
41
42use sa_token_core::router::PathAuthConfig;
43use spring::app::AppBuilder;
44use spring::plugin::MutableComponentRegistry;
45
46/// Trait for configuring Sa-Token path-based authentication
47///
48/// Implement this trait to define your security configuration,
49/// similar to Spring Security's configuration classes.
50///
51/// # Example
52///
53/// ```rust,ignore
54/// use spring_sa_token::{PathAuthBuilder, SaTokenConfigurator};
55///
56/// pub struct SecurityConfig;
57///
58/// impl SaTokenConfigurator for SecurityConfig {
59///     fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
60///         auth.include("/api/**")
61///             .include("/user/**")
62///             .exclude("/login")
63///             .exclude("/public/**")
64///     }
65/// }
66/// ```
67pub trait SaTokenConfigurator: Send + Sync + 'static {
68    /// Configure path-based authentication rules
69    ///
70    /// Receives a PathAuthBuilder and should return it with your rules applied.
71    fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder;
72}
73
74/// Builder for path-based authentication configuration
75///
76/// Provides a fluent API for configuring which paths require authentication.
77///
78/// # Supported Patterns (Ant-style)
79///
80/// - `/**` - Match all paths
81/// - `/api/**` - Match all paths starting with `/api/`
82/// - `/api/*` - Match single-level paths under `/api/` (not nested)
83/// - `*.html` - Match paths ending with `.html`
84/// - `/exact` - Exact match
85///
86/// # Example
87///
88/// ```rust,ignore
89/// let config = PathAuthBuilder::new()
90///     .include("/api/**")
91///     .include("/user/**")
92///     .exclude("/login")
93///     .exclude("/register")
94///     .build();
95/// ```
96#[derive(Debug, Clone, Default)]
97pub struct PathAuthBuilder {
98    pub include: Vec<String>,
99    pub exclude: Vec<String>,
100}
101
102impl PathAuthBuilder {
103    /// Create a new PathAuthBuilder
104    pub fn new() -> Self {
105        Self::default()
106    }
107
108    /// Add a path pattern that requires authentication
109    ///
110    /// # Example
111    ///
112    /// ```rust,ignore
113    /// auth.include("/api/**")  // All paths under /api/ require auth
114    /// ```
115    pub fn include(mut self, pattern: impl Into<String>) -> Self {
116        self.include.push(pattern.into());
117        self
118    }
119
120    /// Add multiple path patterns that require authentication
121    ///
122    /// # Example
123    ///
124    /// ```rust,ignore
125    /// auth.include_all(["/api/**", "/user/**", "/admin/**"])
126    /// ```
127    pub fn include_all(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
128        self.include.extend(patterns.into_iter().map(|p| p.into()));
129        self
130    }
131
132    /// Add a path pattern that is excluded from authentication
133    ///
134    /// # Example
135    ///
136    /// ```rust,ignore
137    /// auth.exclude("/login")  // /login doesn't require auth
138    /// ```
139    pub fn exclude(mut self, pattern: impl Into<String>) -> Self {
140        self.exclude.push(pattern.into());
141        self
142    }
143
144    /// Add multiple path patterns that are excluded from authentication
145    ///
146    /// # Example
147    ///
148    /// ```rust,ignore
149    /// auth.exclude_all(["/login", "/register", "/public/**"])
150    /// ```
151    pub fn exclude_all(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
152        self.exclude.extend(patterns.into_iter().map(|p| p.into()));
153        self
154    }
155
156    /// Alias for `exclude` - paths that don't require authentication (permit all)
157    ///
158    /// Named similarly to Spring Security's `permitAll()`
159    pub fn permit_all(self, pattern: impl Into<String>) -> Self {
160        self.exclude(pattern)
161    }
162
163    /// Alias for `include` - paths that require authentication
164    ///
165    /// Named similarly to Spring Security's `authenticated()`
166    pub fn authenticated(self, pattern: impl Into<String>) -> Self {
167        self.include(pattern)
168    }
169
170    /// Check if any include patterns are configured
171    pub fn is_configured(&self) -> bool {
172        !self.include.is_empty()
173    }
174
175    /// Build the final PathAuthConfig
176    pub fn build(self) -> PathAuthConfig {
177        PathAuthConfig::new()
178            .include(self.include)
179            .exclude(self.exclude)
180    }
181
182    /// Merge another builder's rules into this one
183    pub fn merge(mut self, other: PathAuthBuilder) -> Self {
184        self.include.extend(other.include);
185        self.exclude.extend(other.exclude);
186        self
187    }
188}
189
190// ============================================================================
191// SaTokenAuthConfigurator - Fluent API for configuring path-based auth
192// ============================================================================
193
194/// Trait for configuring Sa-Token path-based authentication via AppBuilder
195///
196/// This provides a fluent API to configure authentication directly on the App.
197///
198/// # Example
199///
200/// Define your security configuration in a separate file:
201///
202/// ```rust,ignore
203/// // security.rs
204/// use spring_sa_token::{PathAuthBuilder, SaTokenConfigurator};
205///
206/// pub struct SecurityConfig;
207///
208/// impl SaTokenConfigurator for SecurityConfig {
209///     fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
210///         auth.include("/user/**")
211///             .include("/admin/**")
212///             .include("/api/**")
213///             .exclude("/")
214///             .exclude("/login")
215///             .exclude("/public/**")
216///             .exclude("/api/health")
217///     }
218/// }
219/// ```
220///
221/// Then use it in main.rs:
222///
223/// ```rust,ignore
224/// // main.rs
225/// mod security;
226/// use spring_sa_token::SaTokenAuthConfigurator;
227///
228/// App::new()
229///     .add_plugin(RedisPlugin)
230///     .add_plugin(SaTokenPlugin)
231///     .add_plugin(WebPlugin)
232///     .sa_token_auth(security::SecurityConfig)
233///     .run()
234///     .await
235/// ```
236pub trait SaTokenAuthConfigurator {
237    /// Configure path-based authentication rules using a SaTokenConfigurator implementation
238    ///
239    /// # Example
240    ///
241    /// ```rust,ignore
242    /// app.sa_token_auth(SecurityConfig);
243    /// ```
244    fn sa_token_auth<C>(&mut self, configurator: C) -> &mut Self
245    where
246        C: SaTokenConfigurator;
247}
248
249impl SaTokenAuthConfigurator for AppBuilder {
250    fn sa_token_auth<C>(&mut self, configurator: C) -> &mut Self
251    where
252        C: SaTokenConfigurator ,
253    {
254        let builder = configurator.configure(PathAuthBuilder::new());
255        if builder.is_configured() {
256            let config = builder.build();
257            self.add_component(config)
258        } else {
259            self
260        }
261    }
262}
263
264impl SaTokenConfigurator for PathAuthBuilder {
265    fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
266        auth.merge(self.clone())
267    }
268}