cursor-init/docs/jwt-usage-examples.md
expressgy a23d336ebd refactor: 重构项目架构并标准化开发规范
- 重构项目结构:controllers/services -> modules模块化组织

- 新增Drizzle ORM集成和数据库schema定义

- 添加完整的开发规范文档(.cursor/rules/)

- 重新组织插件结构为子目录方式

- 新增用户模块和示例代码

- 更新类型定义并移除试验性代码

- 添加API文档和JWT使用示例

关联任务计划文档
2025-06-30 01:25:17 +08:00

6.5 KiB
Raw Blame History

JWT 用户类型使用指南

概述

我们定义了完整的JWT类型系统提供类型安全的JWT操作。

类型定义

1. JwtUserType - JWT中的用户信息

interface JwtUserType {
    userId: number;
    username: string;
    email: string;
    nickname?: string;
    status: number;
    role?: string;
}

2. JwtPayloadType - 完整的JWT载荷

interface JwtPayloadType extends JwtUserType {
    iat: number;  // 发行时间
    exp: number;  // 过期时间  
    sub?: string; // 主题
    iss?: string; // 发行者
    aud?: string; // 受众
    jti?: string; // JWT ID
    nbf?: number; // 生效时间
}

使用示例

1. 在认证Controller中生成JWT Token

// 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中使用用户信息

// 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用户类型

// 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状态检查

// 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. 角色权限检查

// 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. 权限分离: 使用角色字段实现细粒度权限控制