第15章 NestJS 项目部署

打包之前安全配置:CORS&helmet&RateLimit

CORS ------ 跨域资源共享

浏览器出于同源策略 ,默认禁止前端页面向「协议/域名/端口」任一不同的后端发请求。CORS 就是后端通过响应头告诉浏览器:「这个源(TcOrigin)我是允许访问的」。

常见错误配置(务必避免)
错误写法 风险
app.enableCors() 无参数 默认只允许同源,等价于没配,前端一调就跨域报错
origin: '*' + credentials: true 浏览器直接拒绝 ,且 * 等于对任何网站敞开
生产环境还用 origin: true(反射请求源) 任意网站都能带凭证访问你的接口
正确配置(白名单 + 按需开放)
TypeScript 复制代码
const whitelist = ['https://app.example.com', 'https://admin.example.com'];
const corsOptions: CorsOptions = {
  origin: (origin, callback) => {
    // 允许白名单内,或同源/无 origin(如 Postman、服务端调用)
    if (!origin || whitelist.includes(origin)) {
      callback(null, true);
    } else {
      callback(new Error('CORS 不允许该来源'));
    }
  },
  methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
  allowedHeaders: ['Content-Type', 'Authorization'],
  exposedHeaders: ['X-Total-Count'], // 让前端可读的自定义响应头
  credentials: true,                // 允许携带 Cookie / Authorization
  maxAge: 600,                      // 预检请求结果缓存 600 秒
  preflightContinue: false,         // 直接处理 OPTIONS,不往下穿透
};
app.enableCors(corsOptions);

关键点credentials: trueorigin 必须是具体域名 ,不能再用 *。如果是公开 API 不需要 Cookie,则可以 origin: '*' 但务必把 credentials 关掉。

Helmet ------ 安全响应头中间件

Helmet 是 Express 系的安全中间件,它帮你自动设置一组安全相关的 HTTP 响应头 ,堵住浏览器层面的各类攻击面(点击劫持、MIME 嗅探、XSS 探针、降级到 HTTP 等)。NestJS 基于 Express(默认)因此直接 app.use(helmet()) 即可。

响应头 作用
Content-Security-Policy 最核心:限制页面能加载哪些脚本/资源,防 XSS
X-Frame-Options: DENY 禁止被 iframe 嵌套,防点击劫持
Strict-Transport-Security 强制后续只用 HTTPS,防降级攻击
X-Content-Type-Options: nosniff 禁止浏览器猜测 MIME 类型
Referrer-Policy 控制 Referer 泄露程度
X-DNS-Prefetch-Control 关闭 DNS 预取
基础用法
TypeScript 复制代码
import helmet from 'helmet';
app.use(helmet());
生产环境精细化(常需针对 CSP 调整)
TypeScript 复制代码
app.use(
  helmet({
    contentSecurityPolicy: {
      directives: {
        defaultSrc: ["'self'"],
        scriptSrc: ["'self'", "'unsafe-inline'"], // 注意:内联脚本有 XSS 风险
        imgSrc: ["'self'", 'data:', 'https://cdn.example.com'],
        styleSrc: ["'self'", "'unsafe-inline'"],
      },
    },
    // 关闭已不必要的(如纯 API 服务不需要 frameguard 可保留,但跨域策略需明确)
    crossOriginEmbedderPolicy: false,
  }),
);

踩坑提醒 :开启 Content-Security-Policy 后,前端若用了内联脚本、外部 CDN、eval 等,页面会直接挂掉。纯后端 API(无页面)影响不大;若前后端同域带页面,务必和前端联调 CSP 规则。

RateLimit ------ 接口限流

限制单个客户端在给定时间窗口内的请求次数,防御暴力破解、短信轰炸、CC/DoS、爬虫刷接口

推荐方案@nestjs/throttler

TypeScript 复制代码
pnpm add @nestjs/throttler

1) 模块注册(在 AppModule

TypeScript 复制代码
import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler';
import { APP_GUARD } from '@nestjs/core';

@Module({
  imports: [
    ThrottlerModule.forRoot({
      throttlers: [
        { ttl: 60000, limit: 100 }, // 默认:每 60 秒最多 100 次
      ],
      // storage: 默认内存存储,集群/多实例部署要换 RedisStorage
    }),
  ],
  providers: [
    { provide: APP_GUARD, useClass: ThrottlerGuard }, // 全局生效
  ],
})
export class AppModule {}

2) 局部覆盖(针对登录等敏感接口更严格)

TypeScript 复制代码
@Post('signin')
@Throttle({ default: { limit: 5, ttl: 60000 } }) // 登录:每分钟仅 5 次
async signin(@Body() dto: SigninDto) { /* ... */ }

3) 自定义被限流时的响应(默认抛 429,可改文案)

TypeScript 复制代码
import { ThrottlerGuard } from '@nestjs/throttler';

@Injectable()
export class CustomThrottlerGuard extends ThrottlerGuard {
  protected async throwThrottlingException(): Promise<void> {
    throw new HttpException('请求过于频繁,请稍后再试', HttpStatus.TOO_MANY_REQUESTS);
  }
}

部署注意@nestjs/throttler 默认用进程内存 计数。如果你用 PM2 多进程、或 Kubernetes 多副本,每个实例各算各的,限流会失效------此时必须换成 RedisStorage(或 clusterMemory / 外部存储),让计数共享。

一份可直接用的整合 main.ts
TypeScript 复制代码
import { NestFactory } from '@nestjs/core';
import { ValidationPipe } from '@nestjs/common';
import helmet from 'helmet';
import { AppModule } from './app.module';
import { CorsOptions } from 'cors';

const whitelist = ['https://app.example.com'];

async function bootstrap() {
  const app = await NestFactory.create(AppModule, { cors: false });

  // 1. 安全头
  app.use(helmet());

  // 2. CORS(白名单 + 支持凭证)
  app.enableCors({
    origin: (origin, cb) => (!origin || whitelist.includes(origin))
      ? cb(null, true) : cb(new Error('CORS 不允许该来源')),
    methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
    allowedHeaders: ['Content-Type', 'Authorization'],
    credentials: true,
  });

  // 3. 全局校验(配合 DTO,防脏数据注入)
  app.useGlobalPipes(new ValidationPipe({ whitelist: true, transform: true }));

  await app.listen(3000);
}
bootstrap();

项目部署:使用docker进行部署

相关推荐
前端踩bug工程师17 小时前
nestjs 实现一对多,多对一,一对一在实现上的区别
nestjs
爱丶不疚6 天前
Electron:Module 与 Service 的职责边界与加载时序编排
前端·electron·nestjs
沐沐师15 天前
Nest.js 微服务入门教程
微服务·nestjs
沐沐师15 天前
Redis 入门教程
redis·nestjs
谷无姜15 天前
QuizForge:在不停踩坑后的技术决策复盘
前端·nestjs
Flynt17 天前
NestJS 12升级踩坑:从Webpack到Rspack,我折腾了一整个周末
typescript·node.js·nestjs
张洪权18 天前
nest.js websocket 群聊----私聊功能
前端·nestjs
用户712828968446719 天前
NestJS 发布v12版本,看一下有哪些改动
nestjs
YIAN24 天前
NestJS 入门核心梳理:模块化架构、装饰器与依赖注入
后端·nestjs