深入解析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';
     }
}
相关推荐
小白学习日记1 小时前
【复习】HTML常用标签<table>
前端·html
丁总学Java1 小时前
微信小程序-npm支持-如何使用npm包
前端·微信小程序·npm·node.js
yanlele2 小时前
前瞻 - 盘点 ES2025 已经定稿的语法规范
前端·javascript·代码规范
懒羊羊大王呀2 小时前
CSS——属性值计算
前端·css
DOKE2 小时前
VSCode终端:提升命令行使用体验
前端
xgq2 小时前
使用File System Access API 直接读写本地文件
前端·javascript·面试
用户3157476081352 小时前
前端之路-了解原型和原型链
前端
永远不打烊2 小时前
librtmp 原生API做直播推流
前端
北极小狐2 小时前
浏览器事件处理机制:从硬件中断到事件驱动
前端
无咎.lsy2 小时前
vue之vuex的使用及举例
前端·javascript·vue.js