Skip to main content

CORS 跨域资源共享的实现

一、原生 Node.js 实现 CORS

1、实现方式

通过设置 Access-Control-Allow-Origin 响应头来实现 CORS 跨域资源共享:

const http = require('http');

const hostname = '127.0.0.1';
const port = 3000;

const server = http.createServer((req, res) => {
res.statusCode = 200;
res.setHeader('Content-Type', 'text/plain');
res.setHeader('Access-Control-Allow-Origin', '*');
// * 代表所有域,如果设置指定域,可通过以下方式
// res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8001');
res.end('Hello World\n');
});

server.listen(port, hostname, () => {
console.log(`Server running at http://${hostname}:${port}/`);
});

2、应用实例

继前面 Session 做登录验证的代码,点击查看完整前后端代码,将 front-end-project/src/http/config/index.ts 下的请求基础地址与 Server 同步:

const serverConfig = {
// baseURL,
baseURL: 'http://localhost:7676',
useTokenAuthorization: true // 是否开启 token 认证
}

运行:

# 任意目录 启动 redis
redis-server

# server 项目下启动 server
yarn dev

# 前端项目下启动静态页面项目
yarn start

静态项目页面运行在 8001 环境:

服务运行在 7676 环境:

前端 8001 环境调用服务端 7676 环境的接口时,会出现跨域的报错:

通过上面的方式实现 CORS 如下:

server-project/app.js
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', '*')

// ...
}

到这里,在前端 8001 环境可以成功访问到后端 7676 环境的 GET 请求,但 POST 请求会出现以下错误:

这是由于前端发起了复杂请求,触发了 CORS 的预检请求(Preflighted Requests),通过 CORS 解决跨域问题会在发送请求时出现两种情况:简单请求、复杂请求。

3、简单请求与复杂请求

3-1、简单请求

同时满足以下两大条件就属于简单请求。

条件1:使用以下方法之一:

  • GET
  • POST
  • HEAD

条件2:请求的 Header 是:

  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type: 只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain

对于简单请求,直接设置 Access-Control-Allow-Origin 响应头即可实现 CORS。

3-2、复杂请求

不符合以上条件的请求就是复杂请求。

复杂请求的 CORS 请求,会在正式通信之前,增加一次 HTTP 查询请求,称为预检请求,该请求是 option 方法的,通过该请求来知道服务端是否允许跨域请求。解决方式:

server-project/app.js
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');

if (req.method === 'OPTIONS') {
return res.end();
}

// ...
}

默认情况下,跨源请求不提供凭据(cookie、HTTP 认证及客户端 SSL 证明等),如果请求需要使用到 cookie,可以进行以下设置 ▼

在前端请求时设置 withCredentialstrue

front-end-project/src/http/index.ts
const serviceAxios = axios.create({
baseURL: serverConfig.baseURL, // 基础请求地址
timeout: 10000, // 请求超时设置
withCredentials: false, // 跨域请求是否需要携带 cookie
withCredentials: true, // 跨域请求是否需要携带 cookie
});

注意前端请求 withCredentials 设为 true 后,Server 的 Access-Control-Allow-Origin 就不能写 "*" 了,需要指定具体域,然后设置 Access-Control-Allow-Credentials 允许后端跨域传递 Cookie:

server-project/app.js
const serverHandle = (req, res) => {
// ...
res.setHeader('Access-Control-Allow-Origin', 'http://localhost:8001');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type');
res.setHeader("Access-Control-Allow-Credentials", true); // 允许后端跨域传递 Cookie

if (req.method === 'OPTIONS') {
return res.end();
}

// ...
}

到这里,就可以在前端 8001 环境正常访问后端 7676 环境的接口了。点击查看完整代码

二、Express 使用中间件实现

使用 Express 框架时,可以直接使用 cors 中间件实现跨域,安装如下:

npm install cors

使用如下:

app.js
// ...
const cors = require('cors');

// CORS 跨域
app.use(cors({
origin: 'http://localhost:8001',
allowedHeaders: 'Content-Type',
credentials: true
}))

三、Koa 使用中间件实现

Koa 可以使用 @koa/cors 中间件实现跨域,安装如下:

npm install @koa/cors --save

用法与 Express 一样:

app.js
// ...
const cors = require('@koa/cors');

// CORS 跨域
app.use(cors({
// 允许单个域名
origin: 'http://localhost:8001',
// 允许多个域名
// origin: (ctx) => {
// const allowCors = ['http://localhost:8001', 'http://localhost:8002'];
// return allowCors.indexOf(ctx.header.origin) > -1 ? ctx.header.origin : '';
// },
allowedHeaders: 'Content-Type',
credentials: true
}))

点击查看所有跨域实现方式汇总