- 重构项目结构:controllers/services -> modules模块化组织 - 新增Drizzle ORM集成和数据库schema定义 - 添加完整的开发规范文档(.cursor/rules/) - 重新组织插件结构为子目录方式 - 新增用户模块和示例代码 - 更新类型定义并移除试验性代码 - 添加API文档和JWT使用示例 关联任务计划文档
214 lines
6.5 KiB
Markdown
214 lines
6.5 KiB
Markdown
# JWT 用户类型使用指南
|
||
|
||
## 概述
|
||
|
||
我们定义了完整的JWT类型系统,提供类型安全的JWT操作。
|
||
|
||
## 类型定义
|
||
|
||
### 1. JwtUserType - JWT中的用户信息
|
||
```typescript
|
||
interface JwtUserType {
|
||
userId: number;
|
||
username: string;
|
||
email: string;
|
||
nickname?: string;
|
||
status: number;
|
||
role?: string;
|
||
}
|
||
```
|
||
|
||
### 2. JwtPayloadType - 完整的JWT载荷
|
||
```typescript
|
||
interface JwtPayloadType extends JwtUserType {
|
||
iat: number; // 发行时间
|
||
exp: number; // 过期时间
|
||
sub?: string; // 主题
|
||
iss?: string; // 发行者
|
||
aud?: string; // 受众
|
||
jti?: string; // JWT ID
|
||
nbf?: number; // 生效时间
|
||
}
|
||
```
|
||
|
||
## 使用示例
|
||
|
||
### 1. 在认证Controller中生成JWT Token
|
||
|
||
```typescript
|
||
// auth.controller.ts
|
||
import { createJwtPayload } from '@/utils/jwt.helper';
|
||
import type { UserInfoType } from '@/modules/sample/example.schema';
|
||
|
||
export const authController = new Elysia()
|
||
.use(jwtPlugin)
|
||
.post('/login', async ({ body, jwt }) => {
|
||
// 用户登录验证逻辑...
|
||
const userInfo: UserInfoType = await getUserFromDatabase(body.username);
|
||
|
||
// 创建JWT载荷
|
||
const payload = createJwtPayload(userInfo, {
|
||
role: 'user', // 可选的角色信息
|
||
issuer: 'my-api',
|
||
audience: 'web-app',
|
||
});
|
||
|
||
// 生成Token
|
||
const token = await jwt.sign(payload);
|
||
|
||
return {
|
||
code: 0,
|
||
message: '登录成功',
|
||
data: {
|
||
token,
|
||
user: payload, // 返回用户信息(不含敏感数据)
|
||
},
|
||
};
|
||
});
|
||
```
|
||
|
||
### 2. 在需要认证的Controller中使用用户信息
|
||
|
||
```typescript
|
||
// user.controller.ts
|
||
import { formatUserForLog, isValidJwtUser } from '@/utils/jwt.helper';
|
||
import type { JwtUserType } from '@/type/jwt.type';
|
||
|
||
export const userController = new Elysia()
|
||
.use(jwtAuthPlugin)
|
||
.get('/profile', async ({ user, payload }) => {
|
||
// user 自动推断为 JwtUserType 类型
|
||
// payload 自动推断为 JwtPayloadType 类型
|
||
|
||
// 验证用户有效性
|
||
if (!isValidJwtUser(payload)) {
|
||
Logger.warn(`无效用户尝试访问: ${formatUserForLog(user)}`);
|
||
return { code: 401, message: '用户状态异常', data: null };
|
||
}
|
||
|
||
// 使用类型安全的用户信息
|
||
Logger.info(`用户查看个人资料: ${formatUserForLog(user)}`);
|
||
|
||
// 获取完整的用户信息(从数据库)
|
||
const fullUserInfo = await getUserById(user.userId);
|
||
|
||
return {
|
||
code: 0,
|
||
message: '获取成功',
|
||
data: fullUserInfo,
|
||
};
|
||
});
|
||
```
|
||
|
||
### 3. 在Service中使用JWT用户类型
|
||
|
||
```typescript
|
||
// user.service.ts
|
||
import type { JwtUserType } from '@/type/jwt.type';
|
||
|
||
export class UserService {
|
||
// 使用JWT用户类型作为参数
|
||
async updateUserProfile(currentUser: JwtUserType, updateData: any) {
|
||
// 检查权限
|
||
if (currentUser.status !== 1) {
|
||
throw new Error('用户状态异常,无法操作');
|
||
}
|
||
|
||
// 更新用户信息
|
||
const updatedUser = await db.update(users)
|
||
.set(updateData)
|
||
.where(eq(users.id, currentUser.userId));
|
||
|
||
Logger.info(`用户资料更新: ${currentUser.username} (ID: ${currentUser.userId})`);
|
||
return updatedUser;
|
||
}
|
||
|
||
// 根据JWT用户信息获取权限
|
||
async getUserPermissions(jwtUser: JwtUserType): Promise<string[]> {
|
||
const permissions = await db.select()
|
||
.from(userPermissions)
|
||
.where(eq(userPermissions.userId, jwtUser.userId));
|
||
|
||
return permissions.map(p => p.permission);
|
||
}
|
||
}
|
||
```
|
||
|
||
### 4. Token状态检查
|
||
|
||
```typescript
|
||
// middleware/token-check.ts
|
||
import {
|
||
isTokenExpiringSoon,
|
||
getTokenRemainingTime,
|
||
formatRemainingTime
|
||
} from '@/utils/jwt.helper';
|
||
|
||
export const tokenStatusMiddleware = (app: Elysia) =>
|
||
app.derive(({ payload, user }) => {
|
||
if (!payload) return {};
|
||
|
||
// 检查Token是否即将过期
|
||
const expiringSoon = isTokenExpiringSoon(payload, 30); // 30分钟阈值
|
||
const remainingTime = getTokenRemainingTime(payload);
|
||
|
||
if (expiringSoon) {
|
||
Logger.warn(`用户Token即将过期: ${user.username}, 剩余时间: ${formatRemainingTime(remainingTime)}`);
|
||
}
|
||
|
||
return {
|
||
tokenInfo: {
|
||
expiringSoon,
|
||
remainingTime,
|
||
formattedTime: formatRemainingTime(remainingTime),
|
||
},
|
||
};
|
||
});
|
||
```
|
||
|
||
### 5. 角色权限检查
|
||
|
||
```typescript
|
||
// middleware/role-check.ts
|
||
import type { JwtUserType } from '@/type/jwt.type';
|
||
|
||
export function requireRole(requiredRole: string) {
|
||
return (app: Elysia) =>
|
||
app.onBeforeHandle(({ user, set }) => {
|
||
const jwtUser = user as JwtUserType;
|
||
|
||
if (!jwtUser.role || jwtUser.role !== requiredRole) {
|
||
Logger.warn(`权限不足: 用户${jwtUser.username}尝试访问需要${requiredRole}角色的资源`);
|
||
set.status = 403;
|
||
return {
|
||
code: 403,
|
||
message: '权限不足',
|
||
data: null,
|
||
};
|
||
}
|
||
});
|
||
}
|
||
|
||
// 使用示例
|
||
export const adminController = new Elysia()
|
||
.use(jwtAuthPlugin)
|
||
.use(requireRole('admin'))
|
||
.get('/admin-only', () => {
|
||
return { message: '只有管理员能看到这个内容' };
|
||
});
|
||
```
|
||
|
||
## 🎯 类型安全的好处
|
||
|
||
1. **编译时检查**: TypeScript 会在编译时检查所有JWT相关操作
|
||
2. **智能提示**: IDE 提供完整的属性提示和自动补全
|
||
3. **重构安全**: 修改用户类型时,所有相关代码都会得到类型检查
|
||
4. **文档作用**: 类型定义本身就是最好的文档
|
||
|
||
## 📝 最佳实践
|
||
|
||
1. **总是使用类型注解**: 在Service方法中明确使用 `JwtUserType` 类型
|
||
2. **验证用户状态**: 使用 `isValidJwtUser()` 检查用户有效性
|
||
3. **记录操作日志**: 使用 `formatUserForLog()` 格式化用户信息
|
||
4. **检查Token状态**: 在关键操作前检查Token是否即将过期
|
||
5. **权限分离**: 使用角色字段实现细粒度权限控制 |