原文:Understanding CORS 译者:neal1991 welcome to star my articles-translator, providing you advanced articles translation. Any suggestion, please issue or contact me LICENSE: MIT
CORS 或跨域资源共享是一种 http 机制,它允许用户通过使用一些额外的头来访问别的域的资源。例如,假设位于http://test1.domain.com上的应用程序需要对位于 http://test2.domain.com/some/awesome/endpoint上的 api 进行 REST 调用。
现在默认情况下,浏览器不允许这样的请求。这是出于 http 安全原因考虑。这意味着浏览器不允许从网页上的脚本中发出的请求访问位于除最初加载的网站之外的域上的任何 HTTP 资源。例如,XMLHttpRequest 和Fetch API 都遵循同源策略。这就是 CORS 的用武之地。CORS 通过首先使用一些特殊的头来验证test2.domain.com来实现。
和 CORS 相关的头包括
请求头
- Origin
- Access-Control-Request-Method
- Access-Control-Request-Headers
响应头
- Access-Control-Allow-Origin
- Access-Control-Allow-Credentials
- Access-Control-Expose-Headers
- Access-Control-Max-Age
- Access-Control-Allow-Methods
- Access-Control-Allow-Headers
功能概述
CORS 工作的方式是:
- 浏览器遇到一个发送给 test2.domain.com 的请求。
- 它会检查这个请求是否是 GET 或者 HEAD,如果是的话,它将会查找任意自定义 HTTP 头。如果发现任意一个,它将会转到步骤3,否则它会继续处理真实请求,比如步骤 7.
- 浏览器将会使用 Origin, Access-Control-Request-Method * 以及 *Access-Control-Request 头向 test2.domain.com 发送一个 OPTIONS 请求。
- test2.domain.com 必须现在响应合适的 Access-Control- 头。
- 如果在 OPTIONS 请求的响应头中没有发现合适的 Access-Control- 头的话就会错误终止。
- 如果在 OPTIONS 请求的响应头中发现合适的 Access-Control- 头的话就会继续步骤 7。
- 发送真正的请求。
实现
现在,如果test2.domain.com是一个 api 网关,我们可以通过在网关设置中启用 CORS 选项使其与 CORS 兼容。但是,如果你发现自己处于域甚至网关不支持此功能的情况下,请不要担心,仍有一种方法。
你可以在 F5 通过创建 iRule 来插入这些自定义头让test2.domain.com CORS 兼容。
代码语言:javascript复制when HTTP_REQUEST priority 200 { unset -nocomplain cors_origin if { ( [HTTP::header Origin] contains "test1.domain.com" ) } { if { ( [HTTP::method] equals "OPTIONS" ) and ( [HTTP::header exists "Access-Control-Request-Method"] ) } { # CORS preflight request - return response immediately HTTP::respond 200 "Access-Control-Allow-Origin" [HTTP::header "Origin"] "Access-Control-Allow-Methods" "POST, GET, OPTIONS" "Access-Control-Allow-Headers" [HTTP::header "Access-Control-Request-Headers"] "Access-Control-Max-Age" "86400" "Access-Control-Allow-Credentials" "true" } else { # CORS GET/POST requests - set cors_origin variable set cors_origin [HTTP::header "Origin"] log local0. "Requested hostname: [HTTP::host] from IP: [IP::local_addr]" } }}when HTTP_RESPONSE { # CORS GET/POST response - check cors_origin variable set in request if { [info exists cors_origin] } { HTTP::header remove Access-Control-Allow-Origin HTTP::header remove Access-Control-Allow-Credentials HTTP::header remove Vary HTTP::header insert "Access-Control-Allow-Origin" $cors_origin HTTP::header insert "Access-Control-Allow-Credentials" "true" HTTP::header insert "Vary" "Origin" }
这样就可以了.
特殊的例子
我在使用 CORS 时发现了一个非常有趣的案例,我认为这可能值得一提。设置是这样的,我有一个在 domaina 托管的网站。它需要在 domainb 上托管的资源。现在 domain_b 是一个 API 网关,我在网关上启用了开箱即用的 CORS 功能,并认为这样就可以了。我发现除了一个对网关后面的 websphere 服务器上托管的应用程序的资源特殊调用之外,所有对网关的调用都是通过的,这个调用是在。该调用总是错误地出现相同的之前的 CORS 错误:
代码语言:javascript复制 No 'Access-Control-Allow-Origin' header is present on the request resource. Origin '[http://test1.domain.com](http://test1.domain.com/)' is therefore not allowed access.
仔细观察,可以发现响应头中已经丢失了 Access-Control-* 。现在,Websphere 带有自己的 http 服务器,结果证明 http 服务器占用了访问控制头。基于此可以很容易地通过修改 websphere上的 http.conf 来修复。
因此,如果你遇到类似这样的问题,请始终确保验证你的基础架构中是否有任何基础 http/Web服务器。