深入解析CORS(跨域资源共享)是什么?

现代网页使用的外部脚本和资源比以往任何时候都要多。默认情况下,JavaScript 遵循同源策略,只能调用与运行脚本相同域的 URL。那么,我们如何让我们的 JavaScript 强化页面使用外部脚本呢?

CORS就是答案。

跨域资源共享(CORS)是一种机制,允许网页在不同受限域上访问运行的API或资源。

今天,我们将深入探讨CORS,并学习如何在不同的地方使用它。

今天我们将涵盖以下内容:

  • CORS是什么?
  • CORS是如何工作的?
  • CORS请求的类型
  • CORS实现快速指南

CORS是什么?

跨域资源共享(CORS)是一种浏览器机制,允许网页使用其他页面或域中的资源和数据。

大多数网站需要使用外部资源和图片来运行脚本。这些嵌入的资源存在安全风险,因为这些资源可能包含病毒或允许黑客访问服务器。

安全策略可以减轻资源使用的安全风险。该策略根据来源或内容规定了请求站点可以加载哪些资源,并且调控了对请求站点的访问权限。每个策略必须有足够的限制来保护网络服务器,但又不能对功能造成太大影响。

同源策略是一种最安全的政策,可以防止访问任何外部服务器。网站的所有资源必须来自同一源。大多数情况下,同源是一个不错的选择,因为大多数脚本只需要本地资源就可以运行。然而,有时我们希望允许访问外部资源,比如视频、直播或图片。

什么是起源?

原始指的是三个部分:协议、主机和端口号。协议指的是应用层协议,通常是HTTP。主机是所有页面所属的主要站点域名,比如Educative.io。最后,端口号是请求的通信端点,默认为端口80。

许多网站使用一种称为跨域资源共享(CORS)的跨域策略,它定义了网页和主机服务器之间的交互方式,并确定服务器是否安全地允许访问该网页。

CORS是一种在安全性和功能性之间取得平衡的策略,服务器可以批准特定的外部请求,而不会存在批准所有请求的不安全因素。

CORS的实际示例

CORS最常见的例子就是在非本地网站上的广告。

例如,想象一下你正在观看一个视频网站a,然后看到了一个安卓手机的广告。你所在观看视频网站a的服务器被保留用于其必要的资源,无法本地存储所有可能的广告。

相反,所有广告都存储在广告公司的服务器上。广告公司允许访问视频网站a,以便在a网页上播放存储的Android广告视频。

这个系统的好处是a可以使用另一个服务器上的内容,而无需使用本地存储。此外,它还允许广告公司快速推出新的广告,因为他们只需要更新从他们的服务器传递给a的广告内容。

CORS请求可以访问哪些资源?

网站使用CORS请求进行加载:

  • 获取请求或HTTP请求,例如 XMLHTTPRequests
  • 仅支持跨站加载的网络字体和TrueType字体可用
  • Web GL API
  • 图片和视频
  • CSS 绘图

您可以使用CORS在您的网站中自由嵌入这些类型的资源,避免创建本地副本。

CORS是如何工作的?

CORS会将新的HTTP头部添加到标准头部列表中。这些新的CORS头部允许本地服务器保留一份允许的来源列表。

这些来源的任何请求都将被批准,并且他们被允许使用受限资源。要添加到可接受来源列表的标题是 Access-Control-Allow-Origin

有许多不同类型的响应头,可以实现不同级别的访问权限。

以下是一些CORS HTTP头的更多示例:

  • Access-Control-Allow-Credentials
  • Access-Control-Allow-Headers
  • Access-Control-Allow-Methods
  • Access-Control-Expose-Headers
  • Access-Control-Max-Age
  • Access-Control-Request-Headers
  • Access-Control-Request-Method
  • Origin

当一个网络浏览器想要访问一个网站时,它会向网站服务器发送一个CORS请求。如果被允许,这个请求将允许浏览器查看页面,但不能进行其他操作。

大多数服务器允许来自任何来源的 GET 请求,但会阻止其他类型的请求。

服务器将会返回通配符值 * ,这意味着对所请求的数据的访问是无限制的,或者服务器将会检查允许的来源列表。

如果请求者的来源在列表中,允许查看网页,并且服务器会回显允许的来源名称。

如果不允许,服务器将返回一个拒绝访问的消息,说明源是否被禁止访问或者是否被禁止执行特定操作。

CORS请求的类型

上面的请求是最简单的请求形式,只允许查看。还有其他类型的请求,可以进行更复杂的操作,比如跨域请求用于数据操作或删除。

请求类型的分离使我们能够确定每个来源的确切许可级别,并确保每个来源只能执行与其功能相关的请求。

大多数请求可以分为两大类:

  • 简单请求:这些请求不会触发预检请求,并且只使用"安全列表"中的CORS头部。
  • 预检请求:这些请求发送一个"预检"消息,概述了请求者在原始请求之前想要做的事情。被请求的服务器会审查这个预检消息,以确保请求是安全允许的。

简单的请求

简单请求不需要进行预检查,可以使用以下三种方法之一: GETPOST ,和 HEAD 。这些请求是在CORS被发明之前的,因此可以跳过CORS预检查。

GET :

GET 请求要求查看特定URL的共享数据文件的表示形式。它还可以用于触发文件下载。

一个例子就是访问互联网上的任何网站。作为外部用户,我们只能看到网站的内容,无法修改文本或网页元素。

bash 复制代码
GET /index.html

HEAD :

HEAD 请求预览了将与 GET 请求一起发送的标头。它用于在不访问特定URL的情况下,对该URL上存在的内容进行抽样。

例如,您可以使用下载URL来获取其响应头。这样可以在您同意下载之前了解下载文件的大小。

bash 复制代码
HEAD /index.html

POST :

POST 请求要求将数据传输到所请求的服务器,这可能导致服务器发生变化。如果 POST 请求被多次触发,可能会出现意外的行为。

一个例子是在论坛帖子中添加评论。

浏览器向服务器发送请求,要求将您的输入评论添加到服务器上。一旦被接受,论坛服务器会接收到新收到的数据(即评论),并将其存储供他人查看。

response 复制代码
POST /test HTTP/1.1
Host: foo.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2

预检请求

有些方法会生成一个额外的预检请求,该请求会在原始请求之前发送。预检请求是使用 OPTIONS 方法自动生成的,用于那些可能影响用户数据或对服务器进行重大更改的函数。

OPTIONS 方法用于获取有关请求者与服务器交互权限的进一步信息。它返回请求者被批准使用的方法选项。

OPTIONS 是一种安全的方法,意味着它不会改变任何已访问的内容。如果使用预检方法, OPTIONS 将在幕后发送。

当您尝试请求被标记为"需要预检"的方法时,浏览器会自动发出预检请求,您无需手动调用 OPTIONS 方法。

最常见的预检方法是从服务器中删除所选文件或资源。

预检请求包括请求者的来源和所需的方法,使用 Access-Control-Request-Method 进行指示。

服务器分析预检请求,检查该来源是否有权限执行此方法。

如果是的,服务器将返回所有允许使用的方法,并指示您可以发送原始请求。

如果不是这样的话,原始请求将被忽略。

请求者的浏览器可以将这个预检批准缓存,只要它有效。

您可以通过检查 Access-Control-Max-Age 的值来查看批准的到期日期。

请求者的浏览器可以将这个预检批准缓存,只要它有效。您可以通过检查 Access-Control-Max-Age 的值来查看批准的过期日期。

CORS实施快速指南

要开始使用CORS,您需要在应用程序上启用它。以下是来自不同框架的代码片段,可以使您的应用程序支持CORS。

Node.js Express 应用程序:

js 复制代码
app.use(function(req, res, next) {
  res.header("Access-Control-Allow-Origin", "YOUR-DOMAIN.TLD"); // update to match the domain you will make the request from
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept");
  next();
});
app.get('/', function(req, res, next) {
  // Handle the get for this route
});
app.post('/', function(req, res, next) {
 // Handle the post for this route
});

Flask:

安装包:

cmd 复制代码
$ pip install -U flask-cors

然后将其添加到Flask应用程序中:

py 复制代码
# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)

Apache: 阿帕奇

请将以下行添加到您的服务器配置文件的 <Directory><Location><Files><VirtualHost> 部分中。

arduino 复制代码
Header set Access-Control-Allow-Origin "*"

为了确保更改正确应用,请运行 apachectl -t ,然后使用 sudo service apache2 reload 重新加载您的Apache。

使用Kotlin编写的Spring Boot应用程序:

以下的 Kotlin 代码块可以在 Spring Boot 应用程序上启用 CORS。

kotlin 复制代码
import org.springframework.http.HttpMethod
import org.springframework.http.HttpStatus
import org.springframework.stereotype.Component
import org.springframework.web.server.ServerWebExchange
import org.springframework.web.server.WebFilter
import org.springframework.web.server.WebFilterChain
import reactor.core.publisher.Mono
@Component
class CorsFilter : WebFilter {
    override fun filter(ctx: ServerWebExchange?, chain: WebFilterChain?): Mono<Void> {
        if (ctx != null) {
            ctx.response.headers.add("Access-Control-Allow-Origin", "*")
            ctx.response.headers.add("Access-Control-Allow-Methods", "GET, PUT, POST, DELETE, OPTIONS")
            ctx.response.headers.add("Access-Control-Allow-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
            if (ctx.request.method == HttpMethod.OPTIONS) {
                ctx.response.headers.add("Access-Control-Max-Age", "1728000")
                ctx.response.statusCode = HttpStatus.NO_CONTENT
                return Mono.empty()
            } else {
                ctx.response.headers.add("Access-Control-Expose-Headers", "DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Content-Range,Range")
                return chain?.filter(ctx) ?: Mono.empty()
            }
        } else {
            return chain?.filter(ctx) ?: Mono.empty()
        }
    }
}

Nginx:

以下代码块启用了支持预检请求的CORS。

nginx 复制代码
#
# Wide-open CORS config for nginx
#
location / {
     if ($request_method = 'OPTIONS') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        #
        # Custom headers and headers various browsers *should* be OK with but aren't
        #
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        #
        # Tell the client that this pre-flight info is valid for 20 days
        #
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain; charset=utf-8';
        add_header 'Content-Length' 0;
        return 204;
     }
     if ($request_method = 'POST') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
     if ($request_method = 'GET') {
        add_header 'Access-Control-Allow-Origin' '*';
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Range';
        add_header 'Access-Control-Expose-Headers' 'Content-Length,Content-Range';
     }
}
相关推荐
天天向上102412 分钟前
Vue 配置打包后可编辑的变量
前端·javascript·vue.js
芬兰y28 分钟前
VUE 带有搜索功能的穿梭框(简单demo)
前端·javascript·vue.js
好果不榨汁34 分钟前
qiankun 路由选择不同模式如何书写不同的配置
前端·vue.js
小蜜蜂dry34 分钟前
Fetch 笔记
前端·javascript
拾光拾趣录36 分钟前
列表分页中的快速翻页竞态问题
前端·javascript
小old弟36 分钟前
vue3,你看setup设计详解,也是个人才
前端
Lefan41 分钟前
一文了解什么是Dart
前端·flutter·dart
Patrick_Wilson1 小时前
青苔漫染待客迟
前端·设计模式·架构
写不出来就跑路1 小时前
基于 Vue 3 的智能聊天界面实现:从 UI 到流式响应全解析
前端·vue.js·ui
OpenTiny社区1 小时前
盘点字体性能优化方案
前端·javascript