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

image.png

现代网页使用的外部脚本和资源比以往任何时候都要多。默认情况下,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 请求,但会阻止其他类型的请求。

image.png

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

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

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

 CORS请求的类型

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

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

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

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

 简单的请求

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

  GET :

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

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

GET /index.html
GET /index.html
GET /index.html

  HEAD :

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

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

HEAD /index.html
HEAD /index.html
HEAD /index.html

  POST :

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

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

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

POST /test HTTP/1.1
Host: foo.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2
POST /test HTTP/1.1
Host: foo.example.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
field1=value1&field2=value2
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 方法。

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

image.png

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

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

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

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

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

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

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

CORS实施快速指南

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

Node.js Express 应用程序:

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
});
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
});
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:

 安装包:

$ pip install -U flask-cors
$ pip install -U flask-cors
$ pip install -U flask-cors

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

# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)
# app.py
from flask import Flask
from flask_cors import CORS
app = Flask(__name__)
cors = CORS(app)
# app.py from flask import Flask from flask_cors import CORS app = Flask(__name__) cors = CORS(app)

Apache: 阿帕奇

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

Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Origin "*"
Header set Access-Control-Allow-Origin "*"

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

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

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

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()
}
}
}
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()
        }
    }
}
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。

#
# 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';
}
}
#
# 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';
     }
}
# # 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'; } }

© 版权声明
THE END
喜欢就支持一下吧
点赞0

Warning: mysqli_query(): (HY000/3): Error writing file '/tmp/MYaugOnQ' (Errcode: 28 - No space left on device) in /www/wwwroot/583.cn/wp-includes/class-wpdb.php on line 2345
admin的头像-五八三
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

图形验证码
取消
昵称代码图片