CROS

介绍

CORS 是一个 W3C 标准,全称是跨源资源共享Cross-origin resource sharing,或者通俗地称为跨域资源共享。它允许浏览器向跨源的服务器,发出XMLHttpRequest请求,从而克服AJAX只能同源使用的限制。

什么是跨域

为了保证用户信息的安全,所有的浏览器都遵循同源策略。所谓同源是指协议+域名+端口三者都相同,有任何一个不同时,浏览器都视为非同源。此时跨域请求会被阻止。

当你向非同源的服务器发起网络请求的时候,这个请求就是跨域了。

举例:

http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略),dir/page.html是请求资源。它的同源情况:

  • http://www.example.com/dir2/other.html:同源(同一域名)
  • http://example.com/dir/other.html:不同源(域名不同)
  • http://v2.www.example.com/dir/other.html:不同源(域名不同)
  • http://www.example.com:81/dir/other.html:不同源(端口不同)
  • https://www.example.com/dir/page.html:不同源(协议不同)

注:我们在使用postman等工具模拟发起http请求的时候,不会遇到跨域的情况。

CORS的两个请求​

简单请求​​

满足以下条件时,浏览器直接发送请求并检查响应头:

  1. 请求方法为GET、POST、HEAD
  2. 请求头仅包含Accept、Accept-Language、Content-Language、Content-Type(且值为application/x-www-form-urlencodedmultipart/form-datatext/plain)。
    服务器需返回Access-Control-Allow-Origin等头部授权访问。

​非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。浏览器先发送OPTIONS请求验证服务器是否允许。若预检响应通过(如包含Access-Control-Allow-Methods),再发送实际请求。称为预检请求preflight

浏览器先询问服务器,当前网页所在的域名是否在服务器的许可名单之中,以及可以使用哪些HTTP动词和头信息字段。只有得到肯定答复,浏览器才会发出正式的XMLHttpRequest请求,否则就报错。

HTTP请求报文头部

1
2
3
4
5
6
7
8
OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

预检请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。

  1. Origin
    作用:由浏览器自动添加,表示请求的发起源(协议+域名+端口)。
    示例Origin: http://localhost:3000
    触发条件:所有跨域请求均会携带此字段。

  2. Access-Control-Request-Method
    作用:在预检请求(OPTIONS)中声明实际请求将使用的HTTP方法(如PUTDELETE)。
    示例Access-Control-Request-Method: PUT
    触发条件:非简单请求(如使用PUT/DELETE方法)的预检阶段。

  3. Access-Control-Request-Headers
    作用:在预检请求中声明实际请求将携带的自定义请求头(如Authorization)。
    示例Access-Control-Request-Headers: Content-Type, Authorization
    触发条件:请求包含自定义头或非简单Content-Type(如application/json)时触发预检。

HTTP响应报文头部

1
2
3
4
5
6
7
8
9
10
11
12
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2025 11:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
  1. Access-Control-Allow-Origin

    • 作用:指定允许访问资源的源,值可为具体域名或通配符*表示同意任意跨源请求。(带Cookie时不能为*)。
    • 示例Access-Control-Allow-Origin: http://localhost:3000
    • 必填性:所有CORS响应必须包含此字段。
  2. Access-Control-Allow-Methods

    • 作用:声明服务器支持的跨域HTTP方法(如GET, POST, PUT)。
    • 示例Access-Control-Allow-Methods: GET, POST, PUT
    • 必填性:预检请求的响应中必须包含,避免多次预检。
  3. Access-Control-Allow-Headers

    • 作用:声明服务器允许的请求头字段(如Content-TypeAuthorization)。
    • 示例Access-Control-Allow-Headers: Content-Type, Authorization
    • 必填性:当请求包含自定义头时,预检响应必须包含此字段。
  4. Access-Control-Allow-Credentials

    • 作用:指示是否允许浏览器携带Cookie等凭证,值为truefalse
    • 示例Access-Control-Allow-Credentials: true
    • 限制:需与前端withCredentials: true配合使用,且Access-Control-Allow-Origin不能为*
  5. Access-Control-Max-Age

    • 作用:指定预检请求结果的缓存时间(单位:秒),减少重复预检。
    • 示例Access-Control-Max-Age: 86400(缓存24小时)
    • 推荐值:根据业务需求设置,避免过长导致策略更新延迟。
  6. Access-Control-Expose-Headers

    • 作用:允许客户端通过getResponseHeader()访问的响应头(默认仅限6个基础头)。
    • 示例Access-Control-Expose-Headers: X-Custom-Header
    • 适用场景:需暴露自定义响应头时使用。

如果服务器否定了预检请求,会返回一个正常的HTTP回应,但是没有任何CORS相关的头信息字段。这时,浏览器就会认定,服务器不同意预检请求,因此触发一个错误,被XMLHttpRequest对象的onerror回调函数捕获。控制台会打印出如下的报错信息。

1
Access to XMLHttpRequest at 'http://localhost:8080/login' from origin 'http://localhost:5173' has been blocked by CORS policy: Request header field content-type is not allowed by Access-Control-Allow-Headers in preflight response.

一旦服务器通过了“预检”请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

跨域请求的解决方案

C++ Crow库

1
2
3
4
5
6
7
crow::App<crow::CORSHandler> app;
auto &cors = app.get_middleware<crow::CORSHandler>();
cors
.global()
.origin("*")
.headers("origin, x-requested-with, accept, access-control-allow-origin, authorization, content-type")
.methods("POST"_method, "GET"_method, "PUT"_method, "DELETE"_method, "PATCH"_method, "OPTIONS"_method);

正确的HTTP报文

HTTP请求报文头部

1
2
3
4
5
6
7
8
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: origin, x-requested-with, accept, access-control-allow-origin, authorization, content-type
Access-Control-Allow-Methods: POST, GET, PUT, DELETE, PATCH, OPTIONS
Content-Length: 428
Server: Crow/master
Date: Sat, 12 Apr 2025 14:27:50 GMT
Connection: Keep-Alive

HTTP响应报文头部

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
GET / HTTP/1.1
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
Cookie: Pycharm-c8c71706=3f538254-4d73-4a68-b496-2e58ec2a724b; Webstorm-3318ec65=8120e096-872e-4f10-be0a-8f99758d7047
Host: localhost:8080
Sec-Fetch-Dest: document
Sec-Fetch-Mode: navigate
Sec-Fetch-Site: none
Sec-Fetch-User: ?1
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36 Edg/120.0.0.0
sec-ch-ua: "Not_A Brand";v="8", "Chromium";v="120", "Microsoft Edge";v="120"
sec-ch-ua-mobile: ?0
sec-ch-ua-platform: "Windows"

参考:前端跨域解决方案之CORS详解