NestJS CORS配置
学习目标
- 理解CORS(跨域资源共享)的基本概念
- 掌握NestJS中CORS的配置方法
- 学习如何设置CORS的安全策略
- 了解预检请求(OPTIONS)的处理机制
- 掌握CORS配置的最佳实践
核心知识点
1. CORS简介
CORS(Cross-Origin Resource Sharing,跨域资源共享)是一种浏览器安全机制,它允许或限制从一个域向另一个域发起的HTTP请求。CORS的主要目的是保护用户数据的安全,防止恶意网站获取用户在其他网站上的敏感信息。
当浏览器发起跨域请求时,会执行以下步骤:
- 浏览器首先发送一个预检请求(OPTIONS请求)到目标服务器
- 服务器返回CORS头部信息,指示是否允许该跨域请求
- 浏览器根据服务器的响应决定是否发送实际的请求
2. NestJS中的CORS配置
在NestJS中,我们可以通过enableCors()方法来配置CORS:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用CORS
app.enableCors();
await app.listen(3000);
}
bootstrap();3. CORS配置选项
NestJS的CORS配置支持以下选项:
origin:指定允许的源,可以是字符串、字符串数组或函数methods:指定允许的HTTP方法allowedHeaders:指定允许的请求头exposedHeaders:指定暴露给客户端的响应头credentials:指定是否允许携带凭证(如cookie)maxAge:指定预检请求的缓存时间(秒)preflightContinue:指定是否继续处理预检请求optionsSuccessStatus:指定预检请求成功的状态码
4. 详细配置示例
4.1 基本配置
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用CORS并配置选项
app.enableCors({
origin: 'http://localhost:3001', // 只允许来自localhost:3001的请求
methods: ['GET', 'POST', 'PUT', 'DELETE'], // 允许的HTTP方法
allowedHeaders: ['Content-Type', 'Authorization'], // 允许的请求头
credentials: true, // 允许携带凭证
maxAge: 3600, // 预检请求的缓存时间为1小时
});
await app.listen(3000);
}
bootstrap();4.2 允许多个源
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用CORS并允许多个源
app.enableCors({
origin: [
'http://localhost:3001',
'http://localhost:3002',
'https://example.com',
], // 允许的源列表
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
});
await app.listen(3000);
}
bootstrap();4.3 使用函数动态设置源
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 启用CORS并使用函数动态设置源
app.enableCors({
origin: (origin, callback) => {
// 允许的源列表
const allowedOrigins = [
'http://localhost:3001',
'http://localhost:3002',
'https://example.com',
];
// 检查请求源是否在允许列表中
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization'],
credentials: true,
});
await app.listen(3000);
}
bootstrap();5. 预检请求处理
预检请求(Preflight Request)是浏览器在发送实际跨域请求之前发送的OPTIONS请求,用于询问服务器是否允许该跨域请求。预检请求包含以下头部:
Origin:请求的源Access-Control-Request-Method:实际请求将使用的HTTP方法Access-Control-Request-Headers:实际请求将携带的自定义请求头
服务器对预检请求的响应应该包含以下CORS头部:
Access-Control-Allow-Origin:允许的源Access-Control-Allow-Methods:允许的HTTP方法Access-Control-Allow-Headers:允许的请求头Access-Control-Allow-Credentials:是否允许携带凭证Access-Control-Max-Age:预检请求的缓存时间
在NestJS中,当我们启用CORS后,框架会自动处理预检请求,我们不需要手动实现OPTIONS路由。
6. CORS与安全策略
正确配置CORS对于保护应用程序的安全至关重要。以下是一些CORS安全策略的最佳实践:
- 限制允许的源:只允许必要的源,避免使用通配符(
*),特别是当credentials设置为true时 - 限制允许的方法:只允许应用程序实际使用的HTTP方法
- 限制允许的请求头:只允许必要的请求头
- 合理设置缓存时间:设置适当的
maxAge值,减少预检请求的数量 - 使用HTTPS:在生产环境中使用HTTPS,提高安全性
实用案例分析
案例1:前端应用与NestJS API的CORS配置
需求分析
我们有一个前端应用运行在http://localhost:3001,需要与运行在http://localhost:3000的NestJS API进行通信。前端应用需要发送带有认证令牌的请求,并且需要使用多种HTTP方法。
实现方案
- 配置NestJS的CORS:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 配置CORS
app.enableCors({
origin: 'http://localhost:3001', // 只允许前端应用的源
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'], // 允许的HTTP方法
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
], // 允许的请求头
exposedHeaders: ['Content-Length'], // 暴露给客户端的响应头
credentials: true, // 允许携带凭证(如cookie)
maxAge: 3600, // 预检请求的缓存时间为1小时
});
await app.listen(3000);
}
bootstrap();- 前端应用发送请求:
// 前端代码
// 发送GET请求
async function fetchData() {
const response = await fetch('http://localhost:3000/api/data', {
method: 'GET',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here',
},
credentials: 'include', // 包含凭证
});
const data = await response.json();
console.log(data);
}
// 发送POST请求
async function postData() {
const response = await fetch('http://localhost:3000/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer your-token-here',
},
credentials: 'include', // 包含凭证
body: JSON.stringify({ name: 'Test', value: '123' }),
});
const data = await response.json();
console.log(data);
}案例2:生产环境的CORS配置
需求分析
我们需要在生产环境中配置CORS,允许来自特定域名的请求,并确保配置的安全性。
实现方案
- 使用环境变量管理CORS配置:
// src/main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
async function bootstrap() {
const app = await NestFactory.create(AppModule);
// 从环境变量获取允许的源
const allowedOrigins = process.env.CORS_ALLOWED_ORIGINS
? process.env.CORS_ALLOWED_ORIGINS.split(',')
: ['https://example.com'];
// 配置CORS
app.enableCors({
origin: (origin, callback) => {
// 允许的源列表
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
methods: ['GET', 'POST', 'PUT', 'DELETE', 'PATCH'],
allowedHeaders: [
'Content-Type',
'Authorization',
'X-Requested-With',
'Accept',
],
credentials: true,
maxAge: 86400, // 预检请求的缓存时间为24小时
});
await app.listen(3000);
}
bootstrap();- 在部署配置中设置环境变量:
Docker Compose配置:
version: '3'
services:
nestjs-api:
image: nestjs-api:latest
ports:
- '3000:3000'
environment:
- NODE_ENV=production
- CORS_ALLOWED_ORIGINS=https://example.com,https://www.example.comKubernetes配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: nestjs-api
spec:
replicas: 3
selector:
matchLabels:
app: nestjs-api
template:
metadata:
labels:
app: nestjs-api
spec:
containers:
- name: nestjs-api
image: nestjs-api:latest
ports:
- containerPort: 3000
env:
- name: NODE_ENV
value: "production"
- name: CORS_ALLOWED_ORIGINS
value: "https://example.com,https://www.example.com"案例3:使用中间件自定义CORS处理
需求分析
我们需要更灵活地控制CORS处理,例如根据不同的路由设置不同的CORS策略。
实现方案
- 创建CORS中间件:
// src/common/middleware/cors.middleware.ts
import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
@Injectable()
export class CorsMiddleware implements NestMiddleware {
use(req: Request, res: Response, next: NextFunction) {
// 获取请求的源
const origin = req.headers.origin as string;
// 定义允许的源
const allowedOrigins = [
'http://localhost:3001',
'https://example.com',
];
// 检查请求源是否在允许列表中
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// 设置其他CORS头部
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, PATCH, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization, X-Requested-With, Accept');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Max-Age', '86400');
// 处理预检请求
if (req.method === 'OPTIONS') {
return res.status(204).send();
}
next();
}
}- 在应用模块中使用中间件:
// src/app.module.ts
import { Module, NestModule, MiddlewareConsumer } from '@nestjs/common';
import { CorsMiddleware } from './common/middleware/cors.middleware';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { UsersModule } from './users/users.module';
@Module({
imports: [UsersModule],
controllers: [AppController],
providers: [AppService],
})
export class AppModule implements NestModule {
configure(consumer: MiddlewareConsumer) {
// 为所有路由应用CORS中间件
consumer
.apply(CorsMiddleware)
.forRoutes('*');
// 或者为特定路由应用CORS中间件
// consumer
// .apply(CorsMiddleware)
// .forRoutes('api');
}
}常见问题与解决方案
1. CORS错误:No 'Access-Control-Allow-Origin' header
可能原因:
- CORS未启用
- 允许的源配置错误
- 服务器未返回正确的CORS头部
解决方案:
- 确保在NestJS应用中启用了CORS
- 检查
origin配置是否正确,确保包含了前端应用的源 - 检查服务器是否返回了
Access-Control-Allow-Origin头部
2. CORS错误:The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'
可能原因:
- 当
credentials设置为true时,origin不能使用通配符(*)
解决方案:
- 明确指定允许的源,而不是使用通配符
- 或者将
credentials设置为false
3. CORS错误:Method not allowed
可能原因:
- 请求使用的HTTP方法不在允许的方法列表中
解决方案:
- 在CORS配置中添加请求使用的HTTP方法
4. CORS错误:Request header field not allowed
可能原因:
- 请求使用的请求头不在允许的请求头列表中
解决方案:
- 在CORS配置中添加请求使用的请求头
5. 预检请求失败
可能原因:
- 服务器未正确处理OPTIONS请求
- CORS配置错误
解决方案:
- 确保NestJS应用启用了CORS,框架会自动处理预检请求
- 检查CORS配置是否正确
最佳实践
- 环境分离:根据不同的环境(开发、测试、生产)配置不同的CORS策略
- 最小权限原则:只允许必要的源、方法和请求头
- 使用环境变量:将CORS配置存储在环境变量中,便于管理和部署
- 合理设置缓存时间:设置适当的
maxAge值,减少预检请求的数量 - 使用HTTPS:在生产环境中使用HTTPS,提高安全性
- 监控CORS错误:监控和记录CORS错误,及时发现和解决问题
- 文档化CORS配置:记录CORS配置的原因和变更历史
代码优化建议
- 创建CORS配置服务:将CORS配置逻辑封装到专门的服务中,提高代码可维护性
- 使用配置文件:将CORS配置存储在配置文件中,便于管理
- 实现动态CORS配置:根据请求的路由或其他条件动态调整CORS配置
- 添加CORS日志:为CORS请求添加日志,便于调试和监控
- 编写CORS测试:为CORS配置编写单元测试,确保配置的正确性
总结
CORS是现代Web应用开发中不可或缺的一部分,它允许前端应用与后端API进行安全的跨域通信。在NestJS中,配置CORS非常简单,我们可以通过enableCors()方法来启用和配置CORS。
通过本文的学习,你应该已经掌握了:
- CORS的基本概念和工作原理
- NestJS中CORS的配置方法
- CORS配置选项的使用
- 预检请求的处理机制
- CORS安全策略的最佳实践
- 常见CORS问题的解决方案
正确配置CORS对于保护应用程序的安全和确保前端与后端的正常通信至关重要。希望本文对你理解和配置NestJS应用的CORS有所帮助。
互动问答
什么是CORS?它的主要作用是什么?
如何在NestJS中启用CORS?
当
credentials设置为true时,为什么不能使用通配符(*)作为origin?什么是预检请求(OPTIONS请求)?它的作用是什么?
在生产环境中,CORS配置的最佳实践有哪些?