跨域资源共享CORS

1. 同源政策

1.1 含义

  • 1995年,同源政策由 Netscape 公司引入浏览器。目前,所有浏览器都实行这个政策。
  • 最初,它的含义是指,A网页设置的 Cookie,B网页不能打开,除非这两个网页”同源”。所谓”同源”指的是”三个相同”。
协议相同
域名相同
端口相同

举例来说,http://www.example.com/dir/page.html这个网址,协议是http://,域名是www.example.com,端口是80(默认端口可以省略)。它的同源情况如下。

http://www.example.com/dir2/other.html:同源
http://example.com/dir/other.html:不同源(域名不同)
http://v2.www.example.com/dir/other.html:不同源(域名不同)
http://www.example.com:81/dir/other.html:不同源(端口不同)

2. 跨域解决方案CORS

2.1 定义:

  • CORS 是一个 W3C 标准,全称是”跨域资源共享”(Cross-origin resource sharing)。CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。
  • 它允许浏览器向跨源服务器,发出 XMLHttpRequest 请求,从而克服了 AJAX 只能同源使用的限制。整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。

2.2 两种跨域请求

2.2.1 简单请求
  • 同时满足以下条件,那么就是简单请求。
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
(3)没有事件监听器被注册到任何用来发出请求的 XMLHttpRequestUpload 上(经由 XMLHttpRequest.upload 方法取得)上。
(4)请求中没有 ReadableStream 类型的内容被用于上传。

PS:虽然这些都是网页目前已经可以送出的跨站请求,除非后端服务器回复正确CORS标志,否则不会有内容传回来,因此不允许跨域请求的网站无须担心会受到新的HTTP 存取控制的影响。

通常情况下主要涉及条件(1)和条件(2)
如果不满足上述条件任何一个,那么它就是预检请求 (非简单请求)。

浏览器发现自己发送的是简单跨域请求,则会只发送一次HTTP请求。相较于同源请求,CORS简单请求会在头信息中额外增加一个Origin字段。

下图是一个简单跨域请求例子:浏览器发现本次请求是跨域请求,就会自动在请求头信息中增加Origin字段(依赖于浏览器机制/或者JS解释器的实现)
简单跨域

  • 请求和响应内容如下:
    假定是从apigw.qcloud.com去请求qcloud.com的资源
请求头: 
GET /resources/public-data/ HTTP/1.1
Host: qcloud.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://apigw.qcloud.com

响应头:
HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 00:23:53 GMT
Server: Apache/2.0.61 
Access-Control-Allow-Origin: *
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Transfer-Encoding: chunked
Content-Type: application/xml

HTTP请求中的Origin字段表示该请求是来自于http://apigw.qcloud.com的请求
如果Origin指定的域名在许可范围内,服务器返回的响应,会多出几个头信息字段。

Access-Control-Allow-Origin: http://api.bob.com
Access-Control-Allow-Credentials: true
Access-Control-Expose-Headers: FooBar
Content-Type: application/xml

本次请求示例中响应是携带了Access-Control-Allow-Origin: *
或者是 Access-Control-Allow-Origin: http://apigw.qcloud.com

  • 如果Origin指定的源,不在许可范围内,服务器会返回一个正常的HTTP回应。浏览器发现,这个回应的头信息没有包含Access-Control-Allow-Origin字段,就知道出错了,从而抛出一个错误,被XMLHttpRequestonerror回调函数捕获。
2.2.2 预检请求
  • 不满足简单请求条件之一的即是非简单请求。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight)。
  • 「预检(preflight)」请求会先用HTTP 的OPTIONS 方法请求另一个域名资源,确认后续实际(actual)请求能否可安全送出。由于跨域请求可能会携带使用者的信息,所以要先进行预检请求。

下图是一个预检请求例子:
这里写图片描述

请求和响应内容如下:
假定是从apigw.qcloud.com去请求qcloud.com的资源
- 第一次是预检请求/响应:

OPTIONS /resources/post-here/ HTTP/1.1
Host: qcloud.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
Origin: http://apigw.qcloud.com
Access-Control-Request-Method: POST
Access-Control-Request-Headers: X-PINGOTHER, Content-Type


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:39 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://apigw.qcloud.com
Access-Control-Allow-Methods: POST, GET, OPTIONS
Access-Control-Allow-Headers: X-PINGOTHER, Content-Type
Access-Control-Max-Age: 86400
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
  • 先看请求,Access-Control-Request-Method告诉服务器发的请求是POST请求,Access-Control-Request-Headers通知自己带有X-PINGOTHER自定义header

  • 再看响应,Access-Control-Allow-Origin这个与前面类似,Access-Control-Allow-Methods这里说明支持POST/GET/OPTIONS方法,Access-Control-Allow-Headers这里说明允许X-PINGOTHER自定义header,Access-Control-Max-Age用来指定本次预检请求的有效时间,86400是24小时也就是一天。

等到预检请求完成后,浏览器才会发送真正的响应:

POST /resources/post-here/ HTTP/1.1
Host: qcloud.com
User-Agent: Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.5; en-US; rv:1.9.1b3pre) Gecko/20081130 Minefield/3.1b3pre
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7
Connection: keep-alive
X-PINGOTHER: pingpong
Content-Type: text/xml; charset=UTF-8
Content-Length: 55
Origin: http://apigw.qcloud.com
Pragma: no-cache
Cache-Control: no-cache

<?xml version="1.0"?><person><name>Arun</name></person>


HTTP/1.1 200 OK
Date: Mon, 01 Dec 2008 01:15:40 GMT
Server: Apache/2.0.61 (Unix)
Access-Control-Allow-Origin: http://apigw.qcloud.com
Vary: Accept-Encoding, Origin
Content-Encoding: gzip
Content-Length: 235
Keep-Alive: timeout=2, max=99
Connection: Keep-Alive
Content-Type: text/plain

[Some GZIP'd payload]

问题:
1、若简单跨域请求校验失败,APIGW应如何回复?
2、若预检跨域请求校验失败,APIGW应如何回复?
3、目前浏览器并不支持预检请求的重定向,如果发生了预检请求的重定向,则浏览器会大概率报错

3. 跨域请求实现

3.1 原始CORS

1)首先修改 cart-web 的 CartController.java 的 addGoodsToCartList 方法,添加下面两句代码

// 可以访问的域, 此方法不需要访问 cookie
response.setHeader("Access-Control-Allow-Origin", "http://localhost:9105"); 
// 如果要操作 cookie, 则必须加上这句话
response.setHeader("Access-Control-Allow-Credentials", "true");

Access-Control-Allow-Origin :
- Access-Control-Allow-Origin 是 HTML5 中定义的一种解决资源跨域的策略。他是通过服务器端返回带有 Access-Control-Allow-Origin 标识的 Response header,用来解决资源的跨域权限问题。
- 使用方法,在 response 添加 Access-Control-Allow-Origin,例如 :
Access-Control-Allow-Origin:www.google.com , 也可以设置为*表示该资源谁都可以用

2)修改 pyg-item-web 的 itemController.js


    //添加商品到购物车
    $scope.addGoodsToCartList=function(){
        //这是一个跨域的地址
        var url='http://localhost:8086/cart/addGoodsToCartList/'+$scope.sku.id+'/'+$scope.num + '-';
        // {'withCredentials':true} : 是否使用凭证, 需要访问 cookie 时加上
        $http.get(url,{'withCredentials':true}).success(
            function(response){
                if(response.success){
                    location.href="http://localhost:8086/cart.html";
                }else{
                    alert(response.message);
                }
            }
        );
    } 

CORS 请求默认不发送 Cookie 和 HTTP 认证信息。如果要把 Cookie 发到服务器,一方面要服务器同意,指定Access-Control-Allow-Credentials 字段。另一方面,开发者必须在 AJAX 请求中打开withCredentials 属性。否则,即使服务器同意发送 Cookie,浏览器也不会发送。或者,服务器要求设置 Cookie,浏览器也不会处理。

3.2 SpringMVC 跨域注解

  • springMVC 的版本在 4.2 或以上版本,可以使用注解实现跨域, 我们只需要在需要跨域的方法上添加注解@CrossOrigin 即可
// allowCredentials="true" 是否使用凭证, 需要访问cookie时设置, 可以缺省,缺省值时true
@CrossOrigin(origins="http://localhost:9105",allowCredentials="true")

4. 错误信息

1. 无法跨域

无法跨域

2. 重定向不支持跨域请求

重定向不支持跨域请求

参考文献

https://blog.csdn.net/qq_15437667/article/details/78841335

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值