NestJS Redis 原子限流守卫 防刷防攻击

14 阅读3分钟

在后台管理系统与高并发服务中,接口限流是必备的安全防护手段,用于防止恶意请求、暴力破解、CC 攻击,保障服务稳定性。本文将带你基于 NestJS + Redis 原子操作 实现企业级限流守卫,支持接口维度隔离、自动拉黑、跳过限流装饰器

核心实现

1、创建限流常量

// src\common\constant\decorator.constant.ts
export const DecoratorConstant = {
  // ...其他常量
  /** 跳过接口限流 */
  SKIP_THROTTLE: 'common:request:skipThrottle',
}

// src\common\constant\redis.constant.ts
export class RedisConstant {
  // ...其他常量
  /**
   * 接口限流 Key 前缀
   * 拼接客户端IP + 接口路径形成唯一Key,格式为「throttle:limit:ip:接口路径」
   * 用于接口维度的请求频率限制与IP临时拉黑
   */
  public static THROTTLE_LIMIT = 'throttle:limit'
}

2、创建跳过限流装饰器

// src\common\decorator\skip-throttle.decorator.ts
import { SetMetadata } from '@nestjs/common'
import { DecoratorConstant } from '../constant/decorator.constant'

/**
 * 跳过接口限流装饰器
 * 用于控制器或接口方法,标记后不受限流保护拦截
 */
export function SkipThrottle() {
  return SetMetadata(DecoratorConstant.SKIP_THROTTLE, true)
}

3、创建限流核心守卫

import { Reflector } from '@nestjs/core'
import { RedisService } from '@/shared/redis.service'
import { SharedService } from '@/shared/shared.service'
import { RedisConstant } from '../constant/redis.constant'
import { DecoratorConstant } from '../constant/decorator.constant'
import { BusinessException } from '../exception/business.exception'
import { ExecutionContext, Injectable, CanActivate } from '@nestjs/common'

@Injectable()
export class ThrottlerLimitGuard implements CanActivate {
  /** 最大允许请求次数 */
  private readonly MAX_REQUESTS: number = 10
  /** 限流时间窗口(秒) */
  private readonly WINDOW_SECONDS: number = 10
  /** 触发限流后拉黑时长(秒) */
  private readonly LOCK_SECONDS: number = 60 * 30

  constructor(
    private readonly reflector: Reflector,
    private readonly redisService: RedisService,
    private readonly sharedService: SharedService,
  ) {}

  async canActivate(context: ExecutionContext): Promise<boolean> {
    const handler = context.getHandler()
    const controllerClass = context.getClass()
    const request = context.switchToHttp().getRequest<ExpressRequest>()
    const ip = this.sharedService.getRequestIp(request)
    const path = request.route?.path || request.url

    // 判断是否跳过限流
    const skipThrottle = this.reflector.getAllAndOverride<boolean>(DecoratorConstant.SKIP_THROTTLE, [handler, controllerClass])
    if (skipThrottle) return true

    if (!ip) throw new BusinessException('无法获取客户端 IP')

    // 接口维度 + IP 限流(安全隔离)
    const key = `${RedisConstant.THROTTLE_LIMIT}:${ip}:${path}`

    // 检查是否被拉黑
    const data = await this.redisService.get(key)
    if (data === 'locked') throw new BusinessException('请求过于频繁,请稍后再试')

    // 原子自增(高并发安全)
    const count = await this.redisService.incr(key)

    // 第一次设置过期时间
    if (count === 1) await this.redisService.expire(key, this.WINDOW_SECONDS)

    // 超过限制 → 拉黑 30 分钟
    if (count > this.MAX_REQUESTS) {
      await this.redisService.set(key, 'locked', 'EX', this.LOCK_SECONDS)
      throw new BusinessException('请求过于频繁,请稍后再试')
    }

    return true
  }
}

4、全局注册守卫

// src\app.module.ts
import { APP_GUARD } from '@nestjs/core'
import { ThrottlerLimitGuard } from '@/common'

@Module({
  providers: [
    // 注册全局接口限流守卫
    { provide: APP_GUARD, useClass: ThrottlerLimitGuard },
  ],
})
export class AppModule {}

5、装饰器使用示例

import { SkipThrottle } from '@/common'

// 控制器单个接口跳过限流
@Get('info')
@SkipThrottle()
getUserInfo() {
  return this.userService.getInfo()
}

// 整个控制器跳过限流
@Controller('public')
@SkipThrottle()
export class PublicController {}

工作原理

  • 维度隔离:使用 IP + 接口路径 生成唯一 Key,不同接口独立限流,互不干扰
  • 原子操作:基于 Redis INCR 实现原子自增,彻底解决高并发下计数不准问题
  • 智能拉黑:请求超限后自动将 IP 临时拉黑,拒绝恶意攻击
  • 灵活控制:支持全局 / 控制器 / 接口三级跳过限流,适配不同业务场景
  • 安全提示:不暴露具体限流时长,避免给攻击者提供规则信息

核心优势

  • ✅ 高并发安全:Redis 原子操作,无计数错乱问题
  • ✅ 接口维度隔离:精准控制单接口访问频率
  • ✅ 自动封禁:超限自动拉黑,防护能力拉满
  • ✅ 无侵入设计:基于守卫 + 装饰器,不污染业务代码
  • ✅ 全局生效:一次配置,全接口自动防护
  • ✅ 灵活跳过:支持指定接口 / 控制器不限流

使用效果

  • 正常请求:10 秒内≤10 次,正常访问
  • 请求超限:触发限流,返回友好提示
  • 超限多次:自动拉黑当前 IP 30 分钟
  • 标记跳过@SkipThrottle 装饰的接口不受任何限制

总结

本文基于 NestJS 与 Redis 实现的接口限流守卫,仅需少量代码即可完成企业级安全防护,完美解决接口防刷、暴力攻击、恶意请求等问题。 相比 @nestjs/throttler 官方库,本方案更轻量、更安全、更贴合国内后台管理系统需求,可直接应用于生产环境,是后端服务必备的安全组件。