关于 CORS 跨域的一点点理解

如果协议,端口(如果指定了一个)和域名对于两个页面是相同的,则两个页面具有相同的源,请求不同源的资源时涉及跨域请求。跨域请求有很多种,本文重点探讨 CORS 跨域请求。

同源策略

请求不同源的资源时,浏览器的同源策略会对不同请求方式的资源做出限制,有一些标签可以可以嵌入第三方源:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- <script src="..."></script>标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。

- <link rel="stylesheet" href="...">标签嵌入CSS。由于CSS的松散的语法规则,CSS的跨域

需要一个设置正确的Content-Type消息头。不同浏览器有不同的限制: IE, Firefox,

Chrome, Safari(跳至CVE-2010-0051)部分 和 Opera。

- <img>嵌入图片。支持的图片格式包括PNG,JPEG,GIF,BMP,SVG,...

- <video> 和 <audio>嵌入多媒体资源。

- <object>, <embed> 和 <applet>的插件。

- @font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源

字体(same-origin fonts)。

- <frame> 和 <iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。

跨域

XMLHttpRequest 和 Fetch 等都需遵从同源策略(同源策略是基于浏览器的,浏览器或阻止
请求或阻止结果,CSRF 跨站就是阻止了结果,而有 Chrome 等浏览器阻止了 HTTP 到 HTTPS
的请求),如果请求的资源不属于同一个源则为跨域请求。跨域请求需要客户端支持。

非简单请求

  1. 请求方式为 PUT、DELETE、CONNECT、OPTIONS、TRACE、PATCH 中的一种

  2. 人为设置了非安全头信息字段,这些字段除了一下字段都应该被人为是不安全的

    Accept
    Accept-Language
    Content-Language
    Content-Type (but note the additional requirements below)
    DPR
    Downlink
    Save-Data
    Viewport-Width
    Width
    
  3. Content-Type 取值不属于下列之一

    Content-Type 的值不属于下列之一:
    application/x-www-form-urlencoded
    multipart/form-data
    text/plain
    

    对于非简单请求,浏览器先发送一个使用 OPTIONS 方法的“预检请求”,请求的同时在头信息中携带两个字段

    Access-Control-Request-Headers:content-type  // 告知服务器携带自定义非安全字段
    Access-Control-Request-Method: POST // 告知服务器,实际请求将使用 POST 方法
    

    “预检请求”的响应表明请求的源是否被允许跨域,请求自定的非安全字段是否被允许定义以及请求的方法是否被允许等。

    Access-Control-Allow-Headers:Content-Type, Content-Length,
    Authorization, Accept, X-Requested-With , yourHeaderFeild // 跨域请求可以定义这些非安全字段
    Access-Control-Allow-Methods:PUT, POST, GET, DELETE, OPTIONS  // 跨域请求可以使用这些方法
    Access-Control-Allow-Origin:* // 跨域可以是任意源
    Access-Control-Max-Age:86400 // 该时间内无需再次发起“预检请求”
    

    首部字段 Access-Control-Max-Age 表明该响应的有效时间为 86400 秒,也就是 24 小时。在有效时间内,浏览器无须为同一请求再次发起预检请求。请注意,浏览器自身维护了一个最大有效时间,如果该首部字段的值超过了最大有效时间,将不会生效。

简单请求

请求方式是 GET/POST/HEAD 之一 且请求头信息中 Content-Type 取值为 application/x-www-form-urlencoded、multipart/form-data、text/plain中的一种。简单请求与非简单请求最大的区别是简单请求没有预请求。对于简单请求,如果服务器未返回正确的响应首部,则请求方不会收到任何数据。

GET /login HTTP/1.1
Host: 111.111.111.111:80
Content-Type: application/x-www-form-urlencoded
Cache-Control: no-cache
Postman-Token: 52804546-8b1b-7dad-0d96-ea14a4c41241

但 AJAX 发送跨域请求时,浏览器会在请求头信息中添加 origin 字段以表示请求的来源,服务端通过 Access-Control-Allow-Origin 字段设置允许跨域请求的地址。只有当 origin 的取值被包含在 Access-Control-Allow-Origin的值时跨域才被允许。

客户端请求头信息设置

基于 Node 的 Express 头信息设置:

app.all('*', function(req, res, next) {
    res.header("Access-Control-Allow-Origin", "*"); // * 代表允许所有跨域
    res.header("Access-Control-Allow-Headers", "Content-Type,
    Content-Length, Authorization, Accept,X-Requested-With");
    res.header("Access-Control-Allow-Methods",
    "PUT,POST,GET,DELETE,OPTIONS");  // 允许跨域请求的方式
    res.header("X-Powered-By", ' 3.2.1')
    res.header("Content-Type", "application/json;charset=utf-8");
    next();
});

[坑]Node 跨域头的设置

在发送跨域请求时如果是非简单请求将会先发送 OPTIONS “预检请求”,而 Node 服务在没有设置 OPTIONS 路由的情况下前端会有 503 的网关超时提示:

XMLHttpRequest cannot load http://111.111.111.111:8089/login_service. Response to
preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested resource. Origin
'http://111.111.111.111' is therefore not allowed access. The response had HTTP
status code 504.

因此为了避免才坑,后端需要对 OPTIONS 方式进行路由设置或提前返回结果,如下设置:

app.all('*',function (req, res, next) {
    res.header('Access-Control-Allow-Origin', '*');
    res.header('Access-Control-Allow-Headers', 'Content-Type, Content-Length,
    Authorization, Accept, X-Requested-With , yourHeaderFeild');
    res.header('Access-Control-Allow-Methods', 'PUT, POST, GET, DELETE, OPTIONS');

    if (req.method == 'OPTIONS') {
       console.log('you can do that!!');
       res.send(200); //让options请求快速返回
    } else {
        next();
    }
});

参考链接:

本文标题:关于 CORS 跨域的一点点理解

文章作者:Pylon, Syncher

发布时间:2017年08月02日 - 13:08

最后更新:2023年03月11日 - 17:03

原始链接:https://0x400.com/experience/practice/frontend-learn-about-cros/

许可协议: 署名-非商业性使用-禁止演绎 4.0 国际 转载请保留原文链接及作者。