Files
whale-town-end/src/gateway/auth
moyin 5bc7cdb532 fix(auth): 修复AuthGatewayModule依赖注入问题
范围: src/gateway/auth/
- 在AuthGatewayModule中导入LoginCoreModule
- 解决JwtAuthGuard无法注入LoginCoreService的问题
- 确保依赖注入链的完整性
2026-01-14 14:21:35 +08:00
..

认证网关模块 (Auth Gateway Module)

认证网关模块是系统的HTTP API入口负责处理所有认证相关的HTTP请求包括用户登录、注册、密码管理、邮箱验证等功能提供统一的API接口和完善的安全保护机制。

架构层级

Gateway Layer网关层

职责定位

网关层是系统的HTTP API入口负责

  1. 协议处理处理HTTP请求和响应
  2. 数据验证使用DTO进行请求参数验证
  3. 路由管理定义API端点和路由规则
  4. 认证守卫JWT令牌验证和权限检查
  5. 错误转换将业务错误转换为HTTP状态码
  6. API文档提供Swagger API文档

模块组成

src/gateway/auth/
├── login.controller.ts              # 登录API控制器
├── login.controller.spec.ts         # 登录控制器测试
├── register.controller.ts           # 注册API控制器
├── register.controller.spec.ts      # 注册控制器测试
├── jwt_auth.guard.ts                # JWT认证守卫
├── jwt_auth.guard.spec.ts           # JWT认证守卫测试
├── current_user.decorator.ts        # 当前用户装饰器
├── jwt_usage_example.ts             # JWT使用示例开发参考
├── dto/                             # 数据传输对象
│   ├── login.dto.ts                 # 登录相关DTO
│   └── login_response.dto.ts        # 响应DTO
├── auth.gateway.module.ts           # 网关模块配置
└── README.md                        # 模块文档

依赖关系

Gateway Layer (auth.gateway.module)
    ↓ 依赖
Business Layer (auth.module)
    ↓ 依赖
Core Layer (login_core.module)

核心原则

1. 只做协议转换,不做业务逻辑

// ✅ 正确只做HTTP协议处理
@Post('login')
async login(@Body() loginDto: LoginDto, @Res() res: Response): Promise<void> {
  const result = await this.loginService.login({
    identifier: loginDto.identifier,
    password: loginDto.password
  });
  
  this.handleResponse(result, res);
}

// ❌ 错误在Controller中包含业务逻辑
@Post('login')
async login(@Body() loginDto: LoginDto): Promise<any> {
  // 验证用户
  const user = await this.userService.findByIdentifier(loginDto.identifier);
  // 检查密码
  const isValid = await this.comparePassword(loginDto.password, user.password);
  // ... 更多业务逻辑
}

2. 统一的错误处理

private handleResponse(result: any, res: Response, successStatus: HttpStatus = HttpStatus.OK): void {
  if (result.success) {
    res.status(successStatus).json(result);
    return;
  }

  const statusCode = this.getErrorStatusCode(result);
  res.status(statusCode).json(result);
}

3. 使用DTO进行数据验证

export class LoginDto {
  @IsString()
  @IsNotEmpty()
  identifier: string;

  @IsString()
  @IsNotEmpty()
  password: string;
}

对外提供的接口

LoginController

login(loginDto: LoginDto, res: Response): Promise

处理用户登录请求,支持用户名、邮箱或手机号多种方式登录。

githubOAuth(githubDto: GitHubOAuthDto, res: Response): Promise

处理GitHub OAuth登录请求支持使用GitHub账户登录或注册。

refreshToken(refreshTokenDto: RefreshTokenDto, res: Response): Promise

刷新访问令牌,使用有效的刷新令牌生成新的访问令牌。

forgotPassword(forgotPasswordDto: ForgotPasswordDto, res: Response): Promise

发送密码重置验证码到用户邮箱或手机。

resetPassword(resetPasswordDto: ResetPasswordDto, res: Response): Promise

使用验证码重置用户密码。

changePassword(changePasswordDto: ChangePasswordDto, res: Response): Promise

修改用户密码,需要提供旧密码验证。

verificationCodeLogin(verificationCodeLoginDto: VerificationCodeLoginDto, res: Response): Promise

使用邮箱或手机号和验证码进行登录,无需密码。

sendLoginVerificationCode(sendLoginVerificationCodeDto: SendLoginVerificationCodeDto, res: Response): Promise

向用户邮箱或手机发送登录验证码。

debugVerificationCode(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise

获取验证码的详细调试信息,仅用于开发环境。

RegisterController

register(registerDto: RegisterDto, res: Response): Promise

处理用户注册请求,创建新用户账户。

sendEmailVerification(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise

向指定邮箱发送验证码。

verifyEmail(emailVerificationDto: EmailVerificationDto, res: Response): Promise

使用验证码验证邮箱。

resendEmailVerification(sendEmailVerificationDto: SendEmailVerificationDto, res: Response): Promise

重新向指定邮箱发送验证码。

JwtAuthGuard

canActivate(context: ExecutionContext): Promise

验证请求中的JWT令牌提取用户信息并添加到请求上下文。

CurrentUser Decorator

CurrentUser(data?: keyof JwtPayload): ParameterDecorator

从请求上下文中提取当前认证用户信息的参数装饰器。

对外API接口

POST /auth/login

用户登录接口支持用户名、邮箱或手机号多种方式登录返回JWT令牌。

POST /auth/github

GitHub OAuth登录接口使用GitHub账户登录或注册。

POST /auth/verification-code-login

验证码登录接口,使用邮箱或手机号和验证码进行登录,无需密码。

POST /auth/refresh-token

刷新访问令牌接口,使用有效的刷新令牌生成新的访问令牌。

POST /auth/forgot-password

发送密码重置验证码接口,向用户邮箱或手机发送密码重置验证码。

POST /auth/reset-password

重置密码接口,使用验证码重置用户密码。

PUT /auth/change-password

修改密码接口,用户修改自己的密码,需要提供旧密码验证。

POST /auth/send-login-verification-code

发送登录验证码接口,向用户邮箱或手机发送登录验证码。

POST /auth/debug-verification-code

调试验证码信息接口,获取验证码的详细调试信息,仅用于开发环境。

POST /auth/register

用户注册接口,创建新用户账户。

POST /auth/send-email-verification

发送邮箱验证码接口,向指定邮箱发送验证码。

POST /auth/verify-email

验证邮箱接口,使用验证码验证邮箱。

POST /auth/resend-email-verification

重新发送邮箱验证码接口,重新向指定邮箱发送验证码。

使用的项目内部依赖

LoginService (来自 business/auth/login.service)

登录业务服务,提供用户登录、密码管理、令牌刷新等业务逻辑。

RegisterService (来自 business/auth/register.service)

注册业务服务,提供用户注册、邮箱验证等业务逻辑。

LoginCoreService (来自 core/login_core/login_core.service)

登录核心服务提供JWT令牌验证和生成等技术实现。

JwtPayload (来自 core/login_core/login_core.service)

JWT令牌载荷类型定义包含用户ID、用户名、角色等信息。

ThrottlePresets (来自 core/security_core/throttle.decorator)

限流预设配置,提供登录、注册、发送验证码等场景的限流规则。

TimeoutPresets (来自 core/security_core/timeout.decorator)

超时预设配置,提供不同场景的超时时间设置。

核心特性

统一的响应处理机制

  • 智能错误状态码映射根据错误代码和消息自动选择合适的HTTP状态码
  • 统一响应格式所有API返回统一的JSON格式
  • 错误信息标准化:提供清晰的错误代码和消息

完善的安全保护

  • JWT令牌认证使用JWT进行用户身份验证
  • 限流保护防止API滥用和暴力破解
  • 超时控制:防止长时间阻塞和资源占用
  • 请求验证使用DTO和class-validator进行严格的数据验证

完整的API文档

  • Swagger集成自动生成交互式API文档
  • 详细的接口说明每个API都有完整的描述和示例
  • 请求响应示例:提供清晰的数据格式说明

灵活的认证方式

  • 多种登录方式:支持用户名、邮箱、手机号登录
  • 验证码登录:支持无密码的验证码登录
  • OAuth集成支持GitHub OAuth登录
  • 令牌刷新:支持无感知的令牌续期

使用示例

JWT认证完整示例

本模块提供了完整的JWT认证使用示例文件 jwt_usage_example.ts,展示了以下场景:

  1. 公开接口:无需认证的接口
  2. 受保护接口需要JWT令牌的接口
  3. 用户信息获取:使用 @CurrentUser() 装饰器
  4. 特定属性提取:使用 @CurrentUser('username') 获取特定字段
  5. 角色权限检查:基于用户角色的访问控制

详细代码请参考 src/gateway/auth/jwt_usage_example.ts 文件。

在其他模块中使用认证守卫

import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from '../gateway/auth/jwt_auth.guard';
import { CurrentUser } from '../gateway/auth/current_user.decorator';

@Controller('profile')
export class ProfileController {
  @Get()
  @UseGuards(JwtAuthGuard)
  getProfile(@CurrentUser() user: any) {
    return { user };
  }
}

与业务层的交互

网关层通过依赖注入使用业务层服务:

constructor(
  private readonly loginService: LoginService,
  private readonly registerService: RegisterService
) {}

业务层返回统一的响应格式:

interface ApiResponse<T = any> {
  success: boolean;
  data?: T;
  message: string;
  error_code?: string;
}

最佳实践

  1. 保持Controller轻量:只做请求响应处理
  2. 使用DTO验证所有输入都要经过DTO验证
  3. 统一错误处理:使用统一的错误处理方法
  4. 完善API文档使用Swagger装饰器
  5. 限流保护使用Throttle装饰器防止滥用
  6. 超时控制使用Timeout装饰器防止长时间阻塞

潜在风险

限流配置不当风险

  • 限流阈值过低可能影响正常用户使用
  • 限流阈值过高无法有效防止攻击
  • 缓解措施:根据实际业务场景调整限流参数,监控限流触发情况

JWT令牌安全风险

  • 令牌泄露可能导致账户被盗用
  • 令牌过期时间设置不当影响用户体验
  • 缓解措施使用HTTPS传输合理设置令牌过期时间实现令牌刷新机制

验证码安全风险

  • 验证码被暴力破解
  • 验证码发送频率过高导致资源浪费
  • 缓解措施:限制验证码发送频率,增加验证码复杂度,设置验证码有效期

API滥用风险

  • 恶意用户频繁调用API消耗服务器资源
  • 自动化工具批量注册账户
  • 缓解措施:实施限流策略,添加人机验证,监控异常请求模式

错误信息泄露风险

  • 详细的错误信息可能泄露系统实现细节
  • 帮助攻击者了解系统弱点
  • 缓解措施:生产环境使用通用错误消息,详细日志仅记录在服务器端

注意事项

  • 网关层不应该直接访问数据库
  • 网关层不应该包含复杂的业务逻辑
  • 网关层不应该直接调用Core层服务Guard除外
  • 所有业务逻辑都应该在Business层实现