|
@@ -233,19 +233,70 @@ CREATE TABLE users (
|
|
|
);
|
|
);
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
-#### Session表结构
|
|
|
|
|
-```sql
|
|
|
|
|
-CREATE TABLE sessions (
|
|
|
|
|
- id VARCHAR(36) PRIMARY KEY DEFAULT UUID(),
|
|
|
|
|
- user_id VARCHAR(36) NOT NULL,
|
|
|
|
|
- token VARCHAR(500) NOT NULL,
|
|
|
|
|
- expires_at TIMESTAMP NOT NULL,
|
|
|
|
|
- created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
|
|
|
|
- FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE,
|
|
|
|
|
- INDEX idx_user_id (user_id),
|
|
|
|
|
- INDEX idx_token (token),
|
|
|
|
|
- INDEX idx_expires_at (expires_at)
|
|
|
|
|
-);
|
|
|
|
|
|
|
+#### Session存储方案(使用Redis)
|
|
|
|
|
+
|
|
|
|
|
+由于我们使用了Redis,Session信息可以完全存储在Redis中,无需在数据库中创建Session表。Redis提供了更好的性能和自动过期功能。
|
|
|
|
|
+
|
|
|
|
|
+**Redis Session数据结构:**
|
|
|
|
|
+```typescript
|
|
|
|
|
+// Session数据格式
|
|
|
|
|
+interface SessionData {
|
|
|
|
|
+ userId: string;
|
|
|
|
|
+ email: string;
|
|
|
|
|
+ name: string;
|
|
|
|
|
+ role: UserRole;
|
|
|
|
|
+ issuedAt: number; // 时间戳
|
|
|
|
|
+ expiresAt: number; // 时间戳
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// Redis键格式: session:{token}
|
|
|
|
|
+// 设置过期时间与JWT token过期时间一致
|
|
|
|
|
+```
|
|
|
|
|
+
|
|
|
|
|
+**优势:**
|
|
|
|
|
+1. ⚡ 性能更好 - 内存访问 vs 磁盘I/O
|
|
|
|
|
+2. ⏰ 自动过期 - Redis支持TTL自动清理
|
|
|
|
|
+3. 🔍 查询效率 - 基于token直接查找
|
|
|
|
|
+4. 🧹 维护简单 - 无需手动清理过期session
|
|
|
|
|
+5. 📈 扩展性 - Redis集群支持分布式session
|
|
|
|
|
+
|
|
|
|
|
+**Redis配置示例:**
|
|
|
|
|
+```javascript
|
|
|
|
|
+// session服务示例
|
|
|
|
|
+const sessionService = {
|
|
|
|
|
+ async createSession(token: string, user: User, expiresIn: number) {
|
|
|
|
|
+ const sessionData = {
|
|
|
|
|
+ userId: user.id,
|
|
|
|
|
+ email: user.email,
|
|
|
|
|
+ name: user.name,
|
|
|
|
|
+ role: user.role,
|
|
|
|
|
+ issuedAt: Date.now(),
|
|
|
|
|
+ expiresAt: Date.now() + expiresIn * 1000
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ await redis.setex(`session:${token}`, expiresIn, JSON.stringify(sessionData));
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async getSession(token: string) {
|
|
|
|
|
+ const data = await redis.get(`session:${token}`);
|
|
|
|
|
+ return data ? JSON.parse(data) : null;
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async deleteSession(token: string) {
|
|
|
|
|
+ await redis.del(`session:${token}`);
|
|
|
|
|
+ },
|
|
|
|
|
+
|
|
|
|
|
+ async deleteUserSessions(userId: string) {
|
|
|
|
|
+ // 可以使用Redis SCAN命令查找并删除用户的所有session
|
|
|
|
|
+ const keys = await redis.keys(`session:*`);
|
|
|
|
|
+ for (const key of keys) {
|
|
|
|
|
+ const session = await redis.get(key);
|
|
|
|
|
+ if (session && JSON.parse(session).userId === userId) {
|
|
|
|
|
+ await redis.del(key);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+};
|
|
|
```
|
|
```
|
|
|
|
|
|
|
|
## 三、模块设计与交互
|
|
## 三、模块设计与交互
|