CORS
本指南介绍 AdonisJS 应用中的跨域资源共享(CORS)。你将学习如何:
- 安装并配置 CORS 中间件
- 控制允许哪些源、方法和请求头
- 处理跨域请求中的凭据
- 调试常见的 CORS 错误
概述
当浏览器向与当前页面所在域不同的域发起请求时,会强制执行跨域资源共享(CORS)限制。这种安全机制可防止恶意脚本代表用户向你的 API 发起未授权的请求。
例如,如果你的前端运行在 app.example.com,而 API 运行在 api.example.com,浏览器会阻止来自前端的请求,除非你的 API 显式允许该源。在本地开发期间,当前端运行在 localhost:3000 而 API 运行在 localhost:3333 时,同样适用。
在进行某些跨域请求之前,浏览器会使用 OPTIONS HTTP 方法发送一个预检请求(preflight request)。该预检请求会询问你的服务器允许哪些源、方法和请求头。你的服务器必须返回适当的 CORS 响应头,浏览器才会继续发起实际的请求。
AdonisJS 通过 @adonisjs/cors 包来处理 CORS,该包提供的中间件会自动响应预检请求,并为所有响应附加正确的响应头。
安装
使用以下命令安装并配置该包:
node ace add @adonisjs/cors
查看 add 命令执行的步骤
-
使用检测到的包管理器安装
@adonisjs/cors包。 -
在
adonisrc.ts文件中注册以下服务提供者。adonisrc.ts{ providers: [ // ...其他提供者 () => import('@adonisjs/cors/cors_provider') ] } -
创建
config/cors.ts文件。该文件包含 CORS 的配置设置。 -
在
start/kernel.ts文件中注册以下中间件。start/kernel.tsserver.use([ () => import('@adonisjs/cors/cors_middleware') ])
配置
CORS 配置位于 config/cors.ts 中。以下是包含所有可用选项的默认配置:
import app from '@adonisjs/core/services/app'
import { defineConfig } from '@adonisjs/cors'
const corsConfig = defineConfig({
enabled: true,
origin: app.inDev ? true : [],
methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'],
headers: true,
exposeHeaders: [],
credentials: true,
maxAge: 90,
})
export default corsConfig
在开发环境中,origin 被设置为 true 以允许所有源,方便本地前后端搭建。在生产环境中,它默认为空数组 [],即阻止所有跨域请求,直到你显式配置允许的源。
启用和禁用 CORS
enabled 选项可以在不将中间件从中间件栈中移除的情况下将其开启或关闭。当你想在调试期间或特定环境中临时禁用 CORS 时,这会很有用。
{
enabled: process.env.NODE_ENV !== 'test'
}
配置允许的源
origin 选项控制哪些域可以向你的 API 发起跨域请求。这会设置 Access-Control-Allow-Origin 响应头。
要动态允许所有源(响应头会镜像请求发起的源):
{
origin: true
}
要禁止所有跨域请求:
{
origin: false
}
要允许特定的域,提供一个源数组:
{
origin: ['https://app.example.com', 'https://admin.example.com']
}
要使用通配符允许任意源:
{
origin: '*'
}
当 credentials 设置为 true 时,通配符 * 不能作为 Access-Control-Allow-Origin 响应头的值。出于安全原因,浏览器会拒绝这种组合。当同时配置了 origin: '*' 和 credentials: true 时,AdonisJS 会自动通过反射请求发起的源,而不是发送字面的 * 来处理这个问题。
对于动态的源校验,可以提供一个回调函数。当允许的源存储在数据库中,或你需要自定义校验逻辑时,这会很有用:
{
origin: (requestOrigin, ctx) => {
/**
* requestOrigin 是 Origin 头的值。
* 返回 true 表示允许,false 表示拒绝。
*/
const allowedOrigins = ['https://app.example.com']
return allowedOrigins.includes(requestOrigin)
}
}
配置允许的方法
methods 选项指定跨域请求允许使用哪些 HTTP 方法。浏览器的预检请求包含一个 Access-Control-Request-Method 头,服务器会将此值与允许的方法进行比对。
{
methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE']
}
配置允许的请求头
headers 选项控制跨域请求中允许哪些请求头。浏览器的预检请求包含一个 Access-Control-Request-Headers 头,列出了客户端想要发送的请求头。
要允许所有请求头:
{
headers: true
}
要允许特定的请求头:
{
headers: ['Content-Type', 'Accept', 'Authorization']
}
对于动态的请求头校验,提供一个回调:
{
headers: (requestHeaders, ctx) => {
return true
}
}
暴露响应头
默认情况下,浏览器只会向 JavaScript 暴露有限的响应头集合。exposeHeaders 选项允许你指定客户端应能访问的其他响应头。
{
exposeHeaders: ['X-Request-Id', 'X-RateLimit-Remaining']
}
允许凭据
credentials 选项控制是否可以在跨域请求中包含 cookie、Authorization 头以及 TLS 客户端证书。启用后,服务器会发送 Access-Control-Allow-Credentials: true 响应头。
{
credentials: true
}
当你的前端需要向你的 API 发送身份验证 cookie 或 Authorization 头时,启用 credentials。如果不启用,浏览器会从跨域请求中剥离凭据。
缓存预检响应
maxAge 选项指定浏览器应缓存预检响应的时长(以秒为单位)。这会减少重复跨域调用的预检请求数量。
{
maxAge: 90
}
将 maxAge 设置为 null 会完全省略 Access-Control-Max-Age 头。将其设置为 -1 会发送该头但禁用缓存。
常见场景
为单页应用提供 API 服务
当你的 API 和前端部署在不同的域上时,配置 CORS 以允许你前端的源,并携带凭据:
import { defineConfig } from '@adonisjs/cors'
const corsConfig = defineConfig({
enabled: true,
origin: ['https://app.example.com'],
methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'],
headers: true,
credentials: true,
maxAge: 90,
})
export default corsConfig
不同端口的本地开发
在开发期间,你的前端和后端经常运行在不同的端口上。配置 CORS 以允许你的本地前端源:
import { defineConfig } from '@adonisjs/cors'
const corsConfig = defineConfig({
enabled: true,
origin: (requestOrigin) => {
/**
* 在开发期间允许任意端口上的 localhost。
*/
return requestOrigin.startsWith('http://localhost')
},
methods: ['GET', 'HEAD', 'POST', 'PUT', 'DELETE'],
headers: true,
credentials: true,
maxAge: 90,
})
export default corsConfig
无需凭据的公共 API
如果你的 API 是公共的,且不需要 cookie 或身份验证头,你可以使用一个宽松的配置:
import { defineConfig } from '@adonisjs/cors'
const corsConfig = defineConfig({
enabled: true,
origin: '*',
methods: ['GET', 'HEAD', 'POST'],
headers: true,
credentials: false,
maxAge: 86400,
})
export default corsConfig
另请参阅
- MDN CORS 文档 ,深入了解 CORS 的工作原理
- 中间件 ,了解 AdonisJS 中间件系统
- Session ,了解在跨域请求中处理 cookie