sa_token_storage_redis/
lib.rs

1// Author: 金书记
2//
3//! # sa-token-storage-redis
4//! 
5//! Redis存储实现
6//! 
7//! 适用于:
8//! - 分布式部署
9//! - 需要数据持久化
10//! - 高性能要求的场景
11//! 
12//! ## 使用方式
13//! 
14//! ### 方式 1: 使用 Redis URL
15//! ```rust,ignore
16//! use sa_token_storage_redis::RedisStorage;
17//! 
18//! // 无密码
19//! let storage = RedisStorage::new("redis://localhost:6379/0", "sa-token:").await?;
20//! 
21//! // 有密码
22//! let storage = RedisStorage::new("redis://:password@localhost:6379/0", "sa-token:").await?;
23//! ```
24//! 
25//! ### 方式 2: 使用配置结构体
26//! ```rust,ignore
27//! use sa_token_storage_redis::{RedisStorage, RedisConfig};
28//! 
29//! let config = RedisConfig {
30//!     host: "localhost".to_string(),
31//!     port: 6379,
32//!     password: Some("your-password".to_string()),
33//!     database: 0,
34//!     pool_size: 10,
35//! };
36//! 
37//! let storage = RedisStorage::from_config(config, "sa-token:").await?;
38//! ```
39
40use std::time::Duration;
41use async_trait::async_trait;
42use redis::{Client, AsyncCommands, aio::ConnectionManager};
43use serde::{Deserialize, Serialize};
44use sa_token_adapter::storage::{SaStorage, StorageResult, StorageError};
45
46/// Redis 配置
47#[derive(Debug, Clone, Serialize, Deserialize)]
48pub struct RedisConfig {
49    /// Redis 主机地址
50    #[serde(default = "default_host")]
51    pub host: String,
52    
53    /// Redis 端口
54    #[serde(default = "default_port")]
55    pub port: u16,
56    
57    /// Redis 密码(可选)
58    #[serde(default)]
59    pub password: Option<String>,
60    
61    /// 数据库编号
62    #[serde(default)]
63    pub database: u8,
64    
65    /// 连接池大小(暂未使用,保留用于未来扩展)
66    #[serde(default = "default_pool_size")]
67    pub pool_size: u32,
68}
69
70impl Default for RedisConfig {
71    fn default() -> Self {
72        Self {
73            host: default_host(),
74            port: default_port(),
75            password: None,
76            database: 0,
77            pool_size: default_pool_size(),
78        }
79    }
80}
81
82impl RedisConfig {
83    /// 转换为 Redis URL
84    /// 
85    /// 支持的格式:
86    /// - `redis://localhost:6379/0` (无密码)
87    /// - `redis://:password@localhost:6379/0` (有密码)
88    pub fn to_url(&self) -> String {
89        if let Some(password) = &self.password {
90            format!("redis://:{}@{}:{}/{}", password, self.host, self.port, self.database)
91        } else {
92            format!("redis://{}:{}/{}", self.host, self.port, self.database)
93        }
94    }
95}
96
97fn default_host() -> String {
98    "localhost".to_string()
99}
100
101fn default_port() -> u16 {
102    6379
103}
104
105fn default_pool_size() -> u32 {
106    10
107}
108
109/// Redis存储实现
110#[derive(Clone)]
111pub struct RedisStorage {
112    client: ConnectionManager,
113    key_prefix: String,
114}
115
116impl RedisStorage {
117    /// 使用 Redis URL 创建存储
118    /// 
119    /// # 参数
120    /// * `redis_url` - Redis 连接 URL
121    /// * `key_prefix` - 键前缀(例如:`sa-token:`)
122    /// 
123    /// # URL 格式
124    /// - 无密码:`redis://localhost:6379/0`
125    /// - 有密码:`redis://:password@localhost:6379/0`
126    /// - 复杂密码:`redis://:Aq23-hjPwFB3mBDNFp3W1@localhost:6379/0`
127    /// 
128    /// # 示例
129    /// ```rust,ignore
130    /// use sa_token_storage_redis::RedisStorage;
131    /// 
132    /// // 无密码
133    /// let storage = RedisStorage::new("redis://localhost:6379/0", "sa-token:").await?;
134    /// 
135    /// // 有密码
136    /// let storage = RedisStorage::new(
137    ///     "redis://:Aq23-hjPwFB3mBDNFp3W1@localhost:6379/0", 
138    ///     "sa-token:"
139    /// ).await?;
140    /// ```
141    pub async fn new(redis_url: &str, key_prefix: impl Into<String>) -> StorageResult<Self> {
142        let client = Client::open(redis_url)
143            .map_err(|e| StorageError::ConnectionError(e.to_string()))?;
144        
145        let connection_manager = ConnectionManager::new(client).await
146            .map_err(|e| StorageError::ConnectionError(e.to_string()))?;
147        
148        Ok(Self {
149            client: connection_manager,
150            key_prefix: key_prefix.into(),
151        })
152    }
153    
154    /// 使用配置结构体创建存储
155    /// 
156    /// # 参数
157    /// * `config` - Redis 配置
158    /// * `key_prefix` - 键前缀(例如:`sa-token:`)
159    /// 
160    /// # 示例
161    /// ```rust,ignore
162    /// use sa_token_storage_redis::{RedisStorage, RedisConfig};
163    /// 
164    /// let config = RedisConfig {
165    ///     host: "localhost".to_string(),
166    ///     port: 6379,
167    ///     password: Some("Aq23-hjPwFB3mBDNFp3W1".to_string()),
168    ///     database: 0,
169    ///     pool_size: 10,
170    /// };
171    /// 
172    /// let storage = RedisStorage::from_config(config, "sa-token:").await?;
173    /// ```
174    pub async fn from_config(config: RedisConfig, key_prefix: impl Into<String>) -> StorageResult<Self> {
175        let redis_url = config.to_url();
176        Self::new(&redis_url, key_prefix).await
177    }
178    
179    /// 使用构建器模式创建存储
180    /// 
181    /// # 示例
182    /// ```rust,ignore
183    /// use sa_token_storage_redis::RedisStorage;
184    /// 
185    /// let storage = RedisStorage::builder()
186    ///     .host("localhost")
187    ///     .port(6379)
188    ///     .password("Aq23-hjPwFB3mBDNFp3W1")
189    ///     .database(0)
190    ///     .key_prefix("sa-token:")
191    ///     .build()
192    ///     .await?;
193    /// ```
194    pub fn builder() -> RedisStorageBuilder {
195        RedisStorageBuilder::default()
196    }
197    
198    /// 获取完整的键名(带前缀)
199    fn full_key(&self, key: &str) -> String {
200        format!("{}{}", self.key_prefix, key)
201    }
202}
203
204/// Redis 存储构建器
205#[derive(Default)]
206pub struct RedisStorageBuilder {
207    config: RedisConfig,
208    key_prefix: Option<String>,
209}
210
211impl RedisStorageBuilder {
212    /// 设置 Redis 主机地址
213    pub fn host(mut self, host: impl Into<String>) -> Self {
214        self.config.host = host.into();
215        self
216    }
217    
218    /// 设置 Redis 端口
219    pub fn port(mut self, port: u16) -> Self {
220        self.config.port = port;
221        self
222    }
223    
224    /// 设置 Redis 密码
225    pub fn password(mut self, password: impl Into<String>) -> Self {
226        self.config.password = Some(password.into());
227        self
228    }
229    
230    /// 设置数据库编号
231    pub fn database(mut self, database: u8) -> Self {
232        self.config.database = database;
233        self
234    }
235    
236    /// 设置连接池大小(保留用于未来扩展)
237    pub fn pool_size(mut self, size: u32) -> Self {
238        self.config.pool_size = size;
239        self
240    }
241    
242    /// 设置键前缀
243    pub fn key_prefix(mut self, prefix: impl Into<String>) -> Self {
244        self.key_prefix = Some(prefix.into());
245        self
246    }
247    
248    /// 构建 RedisStorage
249    /// 
250    /// # Panics
251    /// 如果未设置 key_prefix,会 panic
252    pub async fn build(self) -> StorageResult<RedisStorage> {
253        let key_prefix = self.key_prefix
254            .expect("key_prefix must be set before building RedisStorage");
255        
256        RedisStorage::from_config(self.config, key_prefix).await
257    }
258}
259
260#[async_trait]
261impl SaStorage for RedisStorage {
262    async fn get(&self, key: &str) -> StorageResult<Option<String>> {
263        let mut conn = self.client.clone();
264        let full_key = self.full_key(key);
265        
266        conn.get(&full_key).await
267            .map_err(|e| StorageError::OperationFailed(e.to_string()))
268    }
269    
270    async fn set(&self, key: &str, value: &str, ttl: Option<Duration>) -> StorageResult<()> {
271        let mut conn = self.client.clone();
272        let full_key = self.full_key(key);
273        
274        if let Some(ttl) = ttl {
275            conn.set_ex(&full_key, value, ttl.as_secs()).await
276                .map_err(|e| StorageError::OperationFailed(e.to_string()))
277        } else {
278            conn.set(&full_key, value).await
279                .map_err(|e| StorageError::OperationFailed(e.to_string()))
280        }
281    }
282    
283    async fn delete(&self, key: &str) -> StorageResult<()> {
284        let mut conn = self.client.clone();
285        let full_key = self.full_key(key);
286        
287        conn.del(&full_key).await
288            .map_err(|e| StorageError::OperationFailed(e.to_string()))
289    }
290    
291    async fn exists(&self, key: &str) -> StorageResult<bool> {
292        let mut conn = self.client.clone();
293        let full_key = self.full_key(key);
294        
295        conn.exists(&full_key).await
296            .map_err(|e| StorageError::OperationFailed(e.to_string()))
297    }
298    
299    async fn expire(&self, key: &str, ttl: Duration) -> StorageResult<()> {
300        let mut conn = self.client.clone();
301        let full_key = self.full_key(key);
302        
303        conn.expire(&full_key, ttl.as_secs() as i64).await
304            .map_err(|e| StorageError::OperationFailed(e.to_string()))
305    }
306    
307    async fn ttl(&self, key: &str) -> StorageResult<Option<Duration>> {
308        let mut conn = self.client.clone();
309        let full_key = self.full_key(key);
310        
311        let ttl_secs: i64 = conn.ttl(&full_key).await
312            .map_err(|e| StorageError::OperationFailed(e.to_string()))?;
313        
314        match ttl_secs {
315            -2 => Ok(None), // 键不存在
316            -1 => Ok(None), // 永不过期
317            secs if secs > 0 => Ok(Some(Duration::from_secs(secs as u64))),
318            _ => Ok(Some(Duration::from_secs(0))),
319        }
320    }
321    
322    async fn mget(&self, keys: &[&str]) -> StorageResult<Vec<Option<String>>> {
323        let mut conn = self.client.clone();
324        let full_keys: Vec<String> = keys.iter().map(|k| self.full_key(k)).collect();
325        
326        conn.get(&full_keys).await
327            .map_err(|e| StorageError::OperationFailed(e.to_string()))
328    }
329    
330    async fn mset(&self, items: &[(&str, &str)], ttl: Option<Duration>) -> StorageResult<()> {
331        let mut conn = self.client.clone();
332        let full_items: Vec<(String, &str)> = items.iter()
333            .map(|(k, v)| (self.full_key(k), *v))
334            .collect();
335        
336        // 使用 pipeline 批量操作
337        let mut pipe = redis::pipe();
338        for (key, value) in &full_items {
339            if let Some(ttl) = ttl {
340                pipe.set_ex(key, *value, ttl.as_secs());
341            } else {
342                pipe.set(key, *value);
343            }
344        }
345        
346        pipe.query_async(&mut conn).await
347            .map_err(|e| StorageError::OperationFailed(e.to_string()))
348    }
349    
350    async fn mdel(&self, keys: &[&str]) -> StorageResult<()> {
351        let mut conn = self.client.clone();
352        let full_keys: Vec<String> = keys.iter().map(|k| self.full_key(k)).collect();
353        
354        conn.del(&full_keys).await
355            .map_err(|e| StorageError::OperationFailed(e.to_string()))
356    }
357    
358    async fn incr(&self, key: &str) -> StorageResult<i64> {
359        let mut conn = self.client.clone();
360        let full_key = self.full_key(key);
361        
362        conn.incr(&full_key, 1).await
363            .map_err(|e| StorageError::OperationFailed(e.to_string()))
364    }
365    
366    async fn decr(&self, key: &str) -> StorageResult<i64> {
367        let mut conn = self.client.clone();
368        let full_key = self.full_key(key);
369        
370        conn.decr(&full_key, 1).await
371            .map_err(|e| StorageError::OperationFailed(e.to_string()))
372    }
373    
374    async fn clear(&self) -> StorageResult<()> {
375        let mut conn = self.client.clone();
376        let pattern = format!("{}*", self.key_prefix);
377        
378        // 获取所有匹配的键
379        let keys: Vec<String> = conn.keys(&pattern).await
380            .map_err(|e| StorageError::OperationFailed(e.to_string()))?;
381        
382        if !keys.is_empty() {
383            conn.del::<_, ()>(&keys).await
384                .map_err(|e| StorageError::OperationFailed(e.to_string()))?;
385        }
386        
387        Ok(())
388    }
389}