CORS处理跨域的配置(学习笔记)

CORS(跨域资源共享)核心配置项


CORS 是浏览器为解决跨域请求限制制定的规范,核心通过服务端响应头控制跨域权限,前端需配合特定配置(如携带 Cookie 时)。以下是服务端核心配置项及说明:

响应头 作用 示例值
Access-Control-Allow-Origin 允许跨域的源(必填),支持具体域名/*(通配符,不可携带 Cookie) https://www.example.com / *
Access-Control-Allow-Methods 允许的请求方法(必填,预检请求用) GET,POST,PUT,DELETE,OPTIONS
Access-Control-Allow-Headers 允许的自定义请求头(预检请求用) Content-Type,Authorization,Token
Access-Control-Allow-Credentials 是否允许携带 Cookie/认证信息(布尔值,需配合前端配置) true
Access-Control-Max-Age 预检请求(OPTIONS)的缓存时间(秒),减少预检请求次数 86400(24小时)
Access-Control-Expose-Headers 允许前端获取的自定义响应头(默认仅能获取6个基础头) X-Total-Count,Token

预检请求(OPTIONS):当跨域请求为「非简单请求」(如 PUT/DELETE、带自定义头、Content-Type 为 application/json)时,浏览器会先发送 OPTIONS 请求校验权限,通过后再发真实请求。

配置「携带 Cookie」的完整步骤


携带 Cookie 是跨域中高频且易出错的场景,必须满足前端 + 服务端双向配置,缺一不可:

前提条件

  1. Access-Control-Allow-Origin 不能用 *,必须指定具体的前端域名(如 https://localhost:3000);
  2. 服务端需设置 Access-Control-Allow-Credentials: true
  3. 前端请求需显式开启「携带凭证」(如 withCredentials: true);
  4. 浏览器要求:跨域 Cookie 需设置 SameSite=None + Secure(HTTPS 环境),否则浏览器会拦截。

步骤1:服务端配置(主流后端示例)

示例1:Node.js/Express
const express = require('express');
const app = express();

// 全局跨域中间件
app.use((req, res, next) => {
  // 1. 允许指定前端域名跨域(不可用*)
  res.setHeader('Access-Control-Allow-Origin', 'https://localhost:3000');
  // 2. 允许携带Cookie
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  // 3. 允许的请求方法
  res.setHeader('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE,OPTIONS');
  // 4. 允许的请求头
  res.setHeader('Access-Control-Allow-Headers', 'Content-Type,Authorization');
  // 5. 预检请求缓存时间
  res.setHeader('Access-Control-Max-Age', '86400');

  // 处理预检请求(OPTIONS)
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200); // 预检请求无需返回内容
  }
  next();
});

// 示例接口:设置Cookie并返回数据
app.get('/api/user', (req, res) => {
  // 设置跨域Cookie(需SameSite=None + Secure,HTTPS环境)
  res.cookie('token', 'abc123', {
    httpOnly: true, // 防止XSS
    secure: true,   // HTTPS环境必填
    sameSite: 'None', // 跨域必填
    maxAge: 24 * 60 * 60 * 1000 // 1天
  });
  res.json({ name: '张三', code: 200 });
});

app.listen(8080, () => console.log('服务端运行在8080端口'));
示例2:Java/Spring Boot
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.context.annotation.Configuration;

@Configuration
public class CorsConfig implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**") // 所有接口
                .allowedOrigins("https://localhost:3000") // 允许的前端域名
                .allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
                .allowedHeaders("Content-Type", "Authorization")
                .allowCredentials(true) // 允许携带Cookie
                .maxAge(86400); // 预检缓存时间
    }
}
示例3:Nginx 反向代理配置(前端通过Nginx转发请求时)
server {
    listen 80;
    server_name api.example.com;

    location / {
        # 跨域配置
        add_header Access-Control-Allow-Origin "https://localhost:3000" always;
        add_header Access-Control-Allow-Credentials "true" always;
        add_header Access-Control-Allow-Methods "GET,POST,PUT,DELETE,OPTIONS" always;
        add_header Access-Control-Allow-Headers "Content-Type,Authorization" always;
        add_header Access-Control-Max-Age "86400" always;

        # 处理预检请求
        if ($request_method = OPTIONS) {
            return 204;
        }

        # 转发到后端服务
        proxy_pass http://127.0.0.1:8080;
        proxy_cookie_domain 127.0.0.1 example.com; # 调整Cookie的Domain
    }
}

步骤2:前端配置(主流前端框架示例)

示例1:原生 Fetch API
fetch('https://api.example.com/api/user', {
  method: 'GET',
  credentials: 'include', // 关键:携带Cookie(同域可用same-origin,跨域需include)
  headers: {
    'Content-Type': 'application/json'
  }
})
.then(res => res.json())
.then(data => console.log(data))
.catch(err => console.error(err));
示例2:Axios(Vue/React常用)
import axios from 'axios';

// 全局配置(推荐)
axios.defaults.withCredentials = true; // 关键:开启携带Cookie

// 或单个请求配置
axios.get('https://api.example.com/api/user', {
  withCredentials: true // 关键
})
.then(res => console.log(res.data))
.catch(err => console.error(err));

三、常见问题 & 解决方案

1. 问题1:浏览器提示「The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*'」
  • 原因:Access-Control-Allow-Origin 设为 * 但同时开启了 Allow-Credentials: true
  • 解决:将 Allow-Origin 改为具体前端域名(如 https://localhost:3000)。
  1. 问题2:Cookie 已返回但前端无法携带

    • 原因1:Cookie 未设置 SameSite=None + Secure(HTTPS 环境);
    • 原因2:前端未设置 withCredentials: true
    • 解决:补全 Cookie 配置 + 前端开启凭证携带。
  2. 问题3:预检请求(OPTIONS)返回404/500

    • 原因:服务端未处理 OPTIONS 请求(非简单请求会先发OPTIONS);
    • 解决:服务端对 OPTIONS 请求直接返回 200/204 状态码(无需业务逻辑)。
  3. 问题4:跨域时 Cookie 的 Domain 不匹配

    • 原因:Cookie 的 Domain 是服务端域名(如 api.example.com),前端域名是 www.example.com
    • 解决:设置 Cookie 的 Domain 为顶级域名(如 .example.com),或通过 Nginx 调整 proxy_cookie_domain

总结

  1. CORS 核心是服务端响应头配置,前端仅需配合开启凭证携带;
  2. 携带 Cookie 必须满足:
    • 服务端:Allow-Origin 为具体域名 + Allow-Credentials: true + Cookie 配置 SameSite=None + Secure
    • 前端:withCredentials: true(Fetch 用 credentials: 'include');
  3. 非简单请求需处理 OPTIONS 预检请求,避免返回 404/500。

什么是预检请求?它在CORS跨域中起到什么作用?


一、什么是预检请求(Preflight Request)

预检请求是浏览器在发送跨域的「非简单请求」前,自动发起的一次 OPTIONS 方法的请求,用于提前向服务端校验:当前跨域请求是否被允许、请求方法/头信息是否合法。只有预检请求通过(服务端返回合法的 CORS 响应头),浏览器才会发送真正的业务请求;若预检失败,浏览器会直接抛出跨域错误,不会执行真实请求。

1. 区分「简单请求」和「非简单请求」

浏览器对跨域请求分为两类,只有非简单请求才会触发预检

类型 判定条件(需同时满足) 示例
简单请求 1. 请求方法为:GET/POST/HEAD
2. 请求头仅包含:Accept/Accept-Language/Content-Language/Content-Type(仅限 application/x-www-form-urlencoded/multipart/form-data/text/plain);
3. 无自定义请求头(如 Authorization/Token)。
GET 请求、普通 POST 表单提交
非简单请求 不满足「简单请求」任一条件的请求 PUT/DELETE 请求、Content-Type: application/jsonPOST 请求、带 Authorization 头的请求

2. 预检请求的特征

  • 请求方法:OPTIONS(HTTP 方法);
  • 请求头包含:
    • Origin:当前前端域名(如 https://localhost:3000);
    • Access-Control-Request-Method:真实请求要使用的方法(如 PUT);
    • Access-Control-Request-Headers:真实请求要携带的自定义头(如 Content-Type,Authorization);
  • 无请求体(仅做权限校验)。

二、预检请求在 CORS 中的核心作用

预检请求是浏览器的安全机制,核心目的是保护服务端免受未授权的跨域请求攻击,同时明确服务端的跨域权限边界,具体作用包括:

1. 提前校验权限,避免无效请求

服务端通过预检请求的响应头,明确告知浏览器:是否允许当前源的跨域请求、允许的方法/头信息是什么。如果权限不匹配,浏览器直接拦截真实请求,避免服务端处理无效的跨域业务请求,减少服务端资源消耗。

2. 明确服务端的跨域策略

预检请求让服务端有机会声明自己的 CORS 规则(如允许的源、方法、头),浏览器会缓存这些规则(通过 Access-Control-Max-Age 配置缓存时间),后续相同的跨域请求无需重复发送预检,提升性能。

3. 防止跨域请求的「意外行为」

比如服务端原本只支持 GET 请求,但前端发起了 DELETE 跨域请求,预检请求会提前发现这种不匹配,避免服务端因处理不支持的请求方法而产生错误。

三、预检请求的完整流程(示例)

以「前端发送 PUT 请求(Content-Type: application/json)到跨域服务端」为例:

  1. 浏览器触发预检
    由于 PUT 方法属于非简单请求,浏览器先发送 OPTIONS 预检请求:

    OPTIONS /api/user/1 HTTP/1.1
    Origin: https://localhost:3000
    Access-Control-Request-Method: PUT
    Access-Control-Request-Headers: Content-Type
    Host: api.example.com
    
  2. 服务端响应预检
    服务端返回包含 CORS 头的响应,告知浏览器权限:

    HTTP/1.1 200 OK
    Access-Control-Allow-Origin: https://localhost:3000
    Access-Control-Allow-Methods: GET,POST,PUT,DELETE
    Access-Control-Allow-Headers: Content-Type,Authorization
    Access-Control-Max-Age: 86400
    Access-Control-Allow-Credentials: true
    
  3. 浏览器校验并发送真实请求
    浏览器检查响应头:

    • Access-Control-Allow-Origin 包含当前源、Allow-Methods 包含 PUTAllow-Headers 包含 Content-Type,则校验通过,发送真实的 PUT 请求;
    • 若校验失败,抛出 Access to XMLHttpRequest at 'xxx' from origin 'xxx' has been blocked by CORS policy 错误。
  4. 服务端处理真实请求并响应
    服务端处理 PUT 请求,返回业务数据,并携带 CORS 头(如 Allow-Origin)。

四、预检请求的常见配置与优化

1. 服务端处理预检请求的关键

  • OPTIONS 方法的请求,无需执行业务逻辑,直接返回 200/204 状态码即可;
  • 必须返回正确的 CORS 响应头(如 Allow-Origin/Allow-Methods/Allow-Headers)。

2. 优化:设置预检缓存时间

通过 Access-Control-Max-Age 头配置预检请求的缓存时间(单位:秒),比如设置为 86400(24小时),浏览器在缓存期内对相同的跨域请求不再重复发送预检,减少请求次数:

// Node.js/Express 示例
res.setHeader('Access-Control-Max-Age', '86400');

3. 常见问题:预检请求返回 404/500

  • 原因:服务端未专门处理 OPTIONS 方法,导致预检请求被当作普通请求处理,返回 404 或 500;
  • 解决:在服务端的跨域中间件中,对 OPTIONS 请求直接返回 200 状态码(如 Express 中 if (req.method === 'OPTIONS') return res.sendStatus(200))。

五、总结

  1. 预检请求是浏览器针对非简单跨域请求的前置校验机制,通过 OPTIONS 方法发起;
  2. 核心作用:提前校验服务端跨域权限,保护服务端安全,减少无效请求
  3. 只有预检请求通过,浏览器才会发送真实的业务请求;
  4. 服务端需正确处理 OPTIONS 请求,并通过 Access-Control-Max-Age 优化预检缓存,提升性能。

简单来说,预检请求就像「跨域请求的安检」,浏览器先问服务端「这个请求能不能过」,服务端同意后,才会让真实请求通行。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容