跨域是很常见的问题,尤其是在web开发中经常会遇到,本文将介绍何为跨域,何为CORS(Cross-Origin Resource Sharing),以及如何在Nginx中配置CORS和在腾讯云COS中配置CORS。

什么是跨域

出于浏览器的同源策略限制。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互。所谓同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)

当一个请求url的协议、域名、端口三者之间任意一个与当前页面url不同即为跨域

当前页面url被请求页面url是否跨域原因
http://www.test.com/http://www.test.com/index.html同源(协议、域名、端口号相同)
http://www.test.com/https://www.test.com/index.html跨域协议不同(http/https)
http://www.test.com/http://www.baidu.com/跨域主域名不同(test/baidu)
http://www.test.com/http://blog.test.com/跨域子域名不同(www/blog)
http://www.test.com:8080/http://www.test.com:7001/跨域端口号不同(8080/7001)

非同源限制

【1】无法读取非同源网页的 Cookie、LocalStorage 和 IndexedDB

【2】无法接触非同源网页的 DOM

【3】无法向非同源地址发送 AJAX 请求

CORS(Cross-Origin Resource Sharing)

CORS 是跨域资源分享(Cross-Origin Resource Sharing)的缩写。它是 W3C 标准,属于跨源 AJAX 请求的根本解决方法。

支持CORS请求的浏览器一旦发现ajax请求跨域,会对请求做一些特殊处理,对于已经实现CORS接口的服务端,接受请求,并做出回应。

有一种情况比较特殊,如果我们发送的跨域请求为“非简单请求”,浏览器会在发出此请求之前首先发送一个请求类型为OPTIONS的“预检请求”,验证请求源是否为服务端允许源,这些对于开发这来说是感觉不到的,由浏览器代理。

总而言之,客户端不需要对跨域请求做任何特殊处理。

简单请求与非简单请求

浏览器对跨域请求区分为“简单请求”与“非简单请求”

“简单请求”满足以下特征:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
(1) 请求方法是以下三种方法之一:
     HEAD
     GET
     POST
(2)HTTP的头信息不超出以下几种字段:
     Accept
     Accept-Language
     Content-Language
     Last-Event-ID
     Content-Type:
         application/x-www-form-urlencoded、 multipart/form-data、text/plain

不满足这些特征的请求称为“非简单请求”,例如:content-type=applicaiton/json , method = PUT/DELETE…

简单请求

浏览器判断跨域为简单请求时候,会在Request Header中添加 Origin (协议 + 域名 + 端口)字段 , 它表示我们的请求源,CORS服务端会将该字段作为跨源标志。

1685088266508632.png

CORS接收到此次请求后 , 首先会判断Origin是否在允许源(由服务端决定)范围之内,如果验证通过,服务端会在Response Header 添加 Access-Control-Allow-Origin、Access-Control-Allow-Credentials等字段。

1685088385651123.png

1
2
3
4
5
必须字段:
Access-Control-Allow-Origin:表示服务端允许的请求源,*标识任何外域,多个源 , 分隔
可选字段
Access-Control-Allow-Credentials:false 表示是否允许发送Cookie,设置为true同时,ajax请求设置withCredentials = true,浏览器的cookie就能发送到服务端
Access-Control-Expose-Headers:调用getResponseHeader()方法时候,能从header中获取的参数

浏览器收到Respnose后会判断自己的源是否存在 Access-Control-Allow-Origin允许源中,如果不存在,会抛出“同源检测异常”。

总结:简单请求只需要CORS服务端在接受到携带Origin字段的跨域请求后,在response header中添加Access-Control-Allow-Origin等字段给浏览器做同源判断。

1685089366140184.png

非简单请求

进行非简单请求时候 , 浏览器会首先发出类型为OPTIONS的“预检请求”,请求地址相同 ,

CORS服务端对“预检请求”处理,并对Response Header添加验证字段,客户端接受到预检请求的返回值进行一次请求预判断,验证通过后,主请求发起。

例如:发起 content-type=application/json 的非简单请求,这时候传参要注意为json字符串

1685089444207377.jpg

这里可以看到,浏览器连续发送了两个jsonp.do请求 , 第一个就是“预检请求”,类型为OPTIONS,因为我们设置了content-type这个属性,所以预检请求的Access-Control-Expose-Headers必须携带content-type,否则预检会失败。

预检通过后,主请求与简单请求一致。

总结:非简单请求需要CORS服务端对OPTIONS类型的请求做处理,其他与简单请求一致

1685089495712124.png

Spring MVC CORS

通过上面叙述,我们得知借助CORS我们不必关心发出的请求是否跨域,浏览器会帮我们处理这些事情,但是服务端需要支持CORS,服务端实现CORS的原理也很简单,在服务端完全可以对请求做上下文处理,已达到接口允许跨域访问的目的。

Nginx配置CORS

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    set $originFlag '';
    if ($http_origin ~* 'https?://(main\.example\.com|main2\.example\.com)') {
        set $originFlag 'corsDomain';
    }
    if ($request_method = 'GET') {
        set $originFlag "{$originFlag}GET_OR_POST";
    }
    if ($request_method = 'POST') {
        set $originFlag "{$originFlag}GET_OR_POST";
    }
    if ($request_method = 'OPTIONS') {
        set $originFlag "{$originFlag}OPTIONS";
    }
    if ($originFlag = '{corsDomain}GET_OR_POST') {
        #set $origin 'https://main.example.com';
        #add_header 'Access-Control-Allow-Origin' $origin;
        add_header 'Access-Control-Allow-Origin' $http_origin;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        add_header 'Access-Control-Allow-Credentials' 'true';
    }
    if ($originFlag = '{corsDomain}OPTIONS') {
        add_header Access-Control-Allow-Origin $http_origin;
        add_header 'Access-Control-Allow-Methods' 'GET, POST, OPTIONS';
        add_header 'Access-Control-Max-Age' 1728000;
        add_header 'Content-Type' 'text/plain charset=UTF-8';
        add_header 'Content-Length' 0;
        add_header 'Access-Control-Allow-Headers' 'DNT,X-CustomHeader,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type';
        add_header 'Access-Control-Allow-Credentials' 'true';
        return 204;
    }

COS配置CORS

1685087516766259.png