Skip to main content

summer_sa_token/
configurator.rs

1//! Sa-Token Configurator module
2//!
3//! This module provides a summer 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//! // config.rs
12//! use summer_sa_token::{PathAuthBuilder, SaTokenConfigurator};
13//!
14//! pub struct SaTokenConfig;
15//!
16//! impl SaTokenConfigurator for SaTokenConfig {
17//!     fn configure_path_auth(&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 config;
31//! use summer_sa_token::SaTokenAuthConfigurator;
32//!
33//! App::new()
34//!     .add_plugin(RedisPlugin)
35//!     .add_plugin(SaTokenPlugin)
36//!     .add_plugin(WebPlugin)
37//!     .sa_token_configure(config::SaTokenConfig)
38//!     .run()
39//!     .await
40//! ```
41
42use sa_token_adapter::storage::SaStorage;
43use sa_token_core::router::PathAuthConfig;
44use summer::app::AppBuilder;
45use summer::plugin::MutableComponentRegistry;
46use std::sync::Arc;
47
48/// Trait for configuring Sa-Token path-based authentication
49///
50/// Implement this trait to define your security configuration,
51/// similar to summer Security's configuration classes.
52///
53/// # Example
54///
55/// ```rust,ignore
56/// use summer_sa_token::{PathAuthBuilder, SaTokenConfigurator};
57///
58/// pub struct SaTokenConfig;
59///
60/// impl SaTokenConfigurator for SaTokenConfig {
61///     fn configure_path_auth(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
62///         auth.include("/api/**")
63///             .include("/user/**")
64///             .exclude("/login")
65///             .exclude("/public/**")
66///     }
67/// }
68/// ```
69pub trait SaTokenConfigurator: Send + Sync + 'static {
70    /// Configure path-based authentication rules (legacy).
71    ///
72    /// Prefer implementing [`SaTokenConfigurator::configure_path_auth`].
73    #[deprecated(note = "use `configure_path_auth` instead")]
74    fn configure(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
75        auth
76    }
77
78    /// Configure path-based authentication rules.
79    ///
80    /// Receives a [`PathAuthBuilder`] and should return it with your rules applied.
81    ///
82    /// By default, this falls back to the legacy [`SaTokenConfigurator::configure`] method,
83    /// so existing implementations keep working.
84    fn configure_path_auth(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
85        #[allow(deprecated)]
86        self.configure(auth)
87    }
88
89    /// Optional hook to provide a custom token storage implementation.
90    ///
91    /// If this returns `Some(storage)`, `summer-sa-token` will register it as a component
92    /// (`Arc<dyn SaStorage>`), and the Sa-Token plugin will prefer it over built-in storages
93    /// (e.g. redis/memory).
94    ///
95    /// Default: `None` (use built-in storage selection).
96    fn configure_storage(&self, _app: &AppBuilder) -> Option<Arc<dyn SaStorage>> {
97        None
98    }
99}
100
101/// Builder for path-based authentication configuration
102///
103/// Provides a fluent API for configuring which paths require authentication.
104///
105/// # Supported Patterns (Ant-style)
106///
107/// - `/**` - Match all paths
108/// - `/api/**` - Match all paths starting with `/api/`
109/// - `/api/*` - Match single-level paths under `/api/` (not nested)
110/// - `*.html` - Match paths ending with `.html`
111/// - `/exact` - Exact match
112///
113/// # Example
114///
115/// ```rust,ignore
116/// let config = PathAuthBuilder::new()
117///     .include("/api/**")
118///     .include("/user/**")
119///     .exclude("/login")
120///     .exclude("/register")
121///     .build();
122/// ```
123#[derive(Debug, Clone, Default)]
124pub struct PathAuthBuilder {
125    pub include: Vec<String>,
126    pub exclude: Vec<String>,
127}
128
129impl PathAuthBuilder {
130    /// Create a new PathAuthBuilder
131    pub fn new() -> Self {
132        Self::default()
133    }
134
135    /// Add a path pattern that requires authentication
136    ///
137    /// # Example
138    ///
139    /// ```rust,ignore
140    /// auth.include("/api/**")  // All paths under /api/ require auth
141    /// ```
142    pub fn include(mut self, pattern: impl Into<String>) -> Self {
143        self.include.push(pattern.into());
144        self
145    }
146
147    /// Add multiple path patterns that require authentication
148    ///
149    /// # Example
150    ///
151    /// ```rust,ignore
152    /// auth.include_all(["/api/**", "/user/**", "/admin/**"])
153    /// ```
154    pub fn include_all(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
155        self.include.extend(patterns.into_iter().map(|p| p.into()));
156        self
157    }
158
159    /// Add a path pattern that is excluded from authentication
160    ///
161    /// # Example
162    ///
163    /// ```rust,ignore
164    /// auth.exclude("/login")  // /login doesn't require auth
165    /// ```
166    pub fn exclude(mut self, pattern: impl Into<String>) -> Self {
167        self.exclude.push(pattern.into());
168        self
169    }
170
171    /// Add multiple path patterns that are excluded from authentication
172    ///
173    /// # Example
174    ///
175    /// ```rust,ignore
176    /// auth.exclude_all(["/login", "/register", "/public/**"])
177    /// ```
178    pub fn exclude_all(mut self, patterns: impl IntoIterator<Item = impl Into<String>>) -> Self {
179        self.exclude.extend(patterns.into_iter().map(|p| p.into()));
180        self
181    }
182
183    /// Alias for `exclude` - paths that don't require authentication (permit all)
184    ///
185    /// Named similarly to summer Security's `permitAll()`
186    pub fn permit_all(self, pattern: impl Into<String>) -> Self {
187        self.exclude(pattern)
188    }
189
190    /// Alias for `include` - paths that require authentication
191    ///
192    /// Named similarly to summer Security's `authenticated()`
193    pub fn authenticated(self, pattern: impl Into<String>) -> Self {
194        self.include(pattern)
195    }
196
197    /// Check if any include patterns are configured
198    pub fn is_configured(&self) -> bool {
199        !self.include.is_empty()
200    }
201
202    /// Build the final PathAuthConfig
203    pub fn build(self) -> PathAuthConfig {
204        PathAuthConfig::new()
205            .include(self.include)
206            .exclude(self.exclude)
207    }
208
209    /// Merge another builder's rules into this one
210    pub fn merge(mut self, other: PathAuthBuilder) -> Self {
211        self.include.extend(other.include);
212        self.exclude.extend(other.exclude);
213        self
214    }
215}
216
217// ============================================================================
218// SaTokenAuthConfigurator - Fluent API for configuring path-based auth
219// ============================================================================
220
221/// Trait for configuring Sa-Token path-based authentication via AppBuilder
222///
223/// This provides a fluent API to configure authentication directly on the App.
224///
225/// # Example
226///
227/// Define your security configuration in a separate file:
228///
229/// ```rust,ignore
230/// // config.rs
231/// use summer_sa_token::{PathAuthBuilder, SaTokenConfigurator};
232///
233/// pub struct SaTokenConfig;
234///
235/// impl SaTokenConfigurator for SaTokenConfig {
236///     fn configure_path_auth(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
237///         auth.include("/user/**")
238///             .include("/admin/**")
239///             .include("/api/**")
240///             .exclude("/")
241///             .exclude("/login")
242///             .exclude("/public/**")
243///             .exclude("/api/health")
244///     }
245/// }
246/// ```
247///
248/// Then use it in main.rs:
249///
250/// ```rust,ignore
251/// // main.rs
252/// mod config;
253/// use summer_sa_token::SaTokenAuthConfigurator;
254///
255/// App::new()
256///     .add_plugin(RedisPlugin)
257///     .add_plugin(SaTokenPlugin)
258///     .add_plugin(WebPlugin)
259///     .sa_token_configure(config::SaTokenConfig)
260///     .run()
261///     .await
262/// ```
263pub trait SaTokenAuthConfigurator {
264    /// Configure path-based authentication rules using a SaTokenConfigurator implementation
265    ///
266    /// # Example
267    ///
268    /// ```rust,ignore
269    /// app.sa_token_configure(config::SaTokenConfig);
270    /// ```
271    #[deprecated(note = "use `sa_token_configure` instead")]
272    fn sa_token_auth<C>(&mut self, configurator: C) -> &mut Self
273    where
274        C: SaTokenConfigurator;
275
276    /// Configure path-based authentication rules using a SaTokenConfigurator implementation
277    ///
278    /// # Example
279    ///
280    /// ```rust,ignore
281    /// app.sa_token_configure(config::SaTokenConfig);
282    /// ```
283    fn sa_token_configure<C>(&mut self, configurator: C) -> &mut Self
284    where
285        C: SaTokenConfigurator;
286}
287
288impl SaTokenAuthConfigurator for AppBuilder {
289    #[allow(deprecated)]
290    fn sa_token_auth<C>(&mut self, configurator: C) -> &mut Self
291    where
292        C: SaTokenConfigurator,
293    {
294        self.sa_token_configure(configurator)
295    }
296
297    fn sa_token_configure<C>(&mut self, configurator: C) -> &mut Self
298    where
299        C: SaTokenConfigurator,
300    {
301        if let Some(storage) = configurator.configure_storage(self) {
302            self.add_component(crate::custom_storage::SaTokenStorage::new(storage));
303        }
304
305        let builder = configurator.configure_path_auth(PathAuthBuilder::new());
306        if builder.is_configured() {
307            let config = builder.build();
308            self.add_component(config)
309        } else {
310            self
311        }
312    }
313}
314
315impl SaTokenConfigurator for PathAuthBuilder {
316    fn configure_path_auth(&self, auth: PathAuthBuilder) -> PathAuthBuilder {
317        auth.merge(self.clone())
318    }
319}