当前位置 : 首页 » 文章分类 :  开发  »  同源策略和CORS跨域

同源策略和CORS跨域

同源策略和CORS跨域解决方案总结


同源策略

同源策略(Same Origin Policy)在web应用的安全模型中是一个重要概念。在这个策略下,web浏览器允许第一个页面的脚本访问第二个页面里的数据,但是也只有在两个页面有相同的源时。源是由URI,主机名,端口号组合而成的。这个策略可以阻止一个页面上的恶意脚本通过页面的DOM对象获得访问另一个页面上敏感信息的权限。

什么情况下算是同源?

所谓同源是指:域名、协议、端口相同。
URL由协议、域名、端口和路径组成,如果两个URL的协议、域名和端口相同,则表示他们同源。相反,只要协议,域名,端口有任何一个的不同,就被当作是跨域。


URL构成

是否跨域

例如,我从我博客 madaimeng.com 访问我的后台api服务 api.devgou.com,被同源策略限制时后台提示如下:


页面访问非同源api被浏览器block

不受同源策略限制的元素

1、页面中的链接,重定向以及表单提交是不会受到同源策略限制的。
2、跨域资源的引入是可以的。但是js不能读写加载的内容。如嵌入到页面中的<script src="..."></script><img><link><iframe>等。

什么是跨域?

受前面所讲的浏览器同源策略的影响,不是同源的脚本不能操作其他源下面的对象。想要操作另一个源下的对象是就需要跨域。

跨域的实现方式

关于跨域,你想知道的全在这里
https://zhuanlan.zhihu.com/p/25778815

关于前端跨域的整理
https://zhuanlan.zhihu.com/p/24198444

九种跨域方式实现原理(完整版)
https://www.jianshu.com/p/cedc8b1cd84c

JSONP

JSONP全称为:JSON with Padding,可用于解决主流浏览器的跨域数据访问的问题。
由于包含src属性的<script>标签可以加载跨域资源。 JSONP就是利用<script>标签的跨域能力实现跨域数据的访问。

Web 页面上调用 js 文件不受浏览器同源策略的影响,所以通过 Script 便签可以进行跨域的请求:
1、首先前端先设置好回调函数,并将其作为 url 的参数。
2、服务端接收到请求后,通过该参数获得回调函数名,并将数据放在参数中将其返回
3、收到结果后因为是 script 标签,所以浏览器会当做是脚本进行运行,从而达到跨域获取数据的目的。

JSONP的缺点
JSONP只支持 GET 请求。

window.name

window对象有个name属性,该属性有个特征:即在一个窗口(window)的生命周期内,窗口载入的所有的页面都是共享一个 window.name 的,每个页面对 window.name 都有读写的权限,window.name 是持久存在一个窗口载入过的所有页面中的,并不会因新页面的载入而进行重置。并且可以支持非常长的 name 值(2MB)。

通过 iframe 的 src 属性由外域转向本地域,跨域数据即由 iframe 的 window.name从外域传递到本地域。这个就巧妙地绕过了浏览器的跨域访问限制,但同时它又是安全操作。

document.domain:

对于主域相同而子域不同的情况下,可以通过设置 document.domain 的办法来解决,具体做法是可以在 http://www.example.com/a.htmlhttp://sub.example.com/b.html 两个文件分别加上 document.domain = “a.com”;然后通过 a.html 文件创建一个 iframe,去控制 iframe 的 window,从而进行交互,当然这种方法只能解决主域相同而二级域名不同的情况。

CORS(跨域资源共享)

CORS(Cross-Origin Resource Sharing,跨域资源共享)是一个 W3C 标准,定义了在必须访问跨域资源时,浏览器与服务器应该如何沟通。CORS 背后的基本思想,就是使用自定义的 HTTP 头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功,还是应该失败。

CORS 需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE 浏览器不能低于 IE10。

整个 CORS 通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS 通信与同源的 AJAX 通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。

因此,实现 CORS 通信的关键是服务器。只要服务器实现了 CORS 接口,就可以跨源通信。


CORS详解与实例

其实CORS很好理解,服务端在HTTP响应中添加一个Header Access-Control-Allow-Origin:http://www.devgou.com header值是允许的源,浏览器看到自己的源和服务端返回的能匹配上,就允许跨域。

浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。

简单请求和复杂请求

只要同时满足以下两大条件,就属于简单请求。
一、请求方法是以下三种方法之一:

  • HEAD
  • GET
  • POST

二、HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • Content-Type:只限于三个值 application/x-www-form-urlencodedmultipart/form-datatext/plain

凡是不同时满足上面两个条件,就属于非简单请求。
浏览器对这两种请求的处理,是不一样的。

简单请求

对于简单的跨域请求,浏览器会自动在请求的头信息加上 Origin 字段,表示本次请求来自哪个源(协议 + 域名 + 端口),服务端应该获取到这个值,然后判断是否同意这次请求并返回。

简单请求处理过程:
1、浏览器自动在跨域请求中附加一个的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。例如:Origin: http://www.devgou.com
2、如果服务器认为这个请求可以接受,就在http响应中添加header Access-Control-Allow-Origin ,其值设为和请求中Origin相同的源信息(如果是公共资源,可以设置 * 表示接受任意域名的请求 )。例如:Access-Control-Allow-Origin:http://www.devgou.com
3、没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。注意,请求和响应都不包含 cookie 信息。
4、如果需要包含 cookie 信息,ajax 请求需要设置 XMLHttpRequest 的属性 withCredentialstrue,服务器需要设置响应头部 Access-Control-Allow-Credentials: true

复杂请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是 application/json。
复杂请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为”预检”请求(preflight),预检请求使用的http方法 OPTIONS,通过该请求来知道服务端是否允许跨域请求。

复杂请求处理过程:
1、浏览器发送预检请求
浏览器在发送真正的请求之前,先发送一个 Preflight 预检请求给服务器,这种请求使用 OPTIONS 方法,发送下列头部:
Origin:必须,与简单的请求相同,值为请求页面的源信息(协议、域名和端口。
Access-Control-Request-Method: 必须,请求自身使用的http方法,例如PUT。
Access-Control-Request-Headers: 可选,自定义的头部信息,多个头部以逗号分隔。
预检请求示例:

OPTIONS /cors HTTP/1.1
Origin: https://devgou.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header

2、服务端返回预检响应
收到这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通:
Access-Control-Allow-Origin:必须,与简单的请求相同,允许访问的源。
Access-Control-Allow-Methods: 必须,允许的方法,多个方法以逗号分隔。表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次”预检”请求。
Access-Control-Allow-Headers: 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在”预检”中请求的字段。
Access-Control-Max-Age: 可选,用来指定本次预检请求的有效期,单位为秒。或者说应该将这个 Preflight 请求缓存多长时间。
预检请求响应示例:

Access-Control-Allow-Origin: http://devgou.com
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

3、浏览器的正常请求和回应
一旦服务器通过了”预检”请求,以后每次浏览器正常的CORS请求,就都跟简单请求一样,会有一个Origin头信息字段。服务器的回应,也都会有一个Access-Control-Allow-Origin头信息字段。

浏览器同源策略及跨域的解决方法
http://www.cnblogs.com/laixiangran/p/9064769.html

关于前端跨域的整理
https://zhuanlan.zhihu.com/p/24198444


CORS配置实例

前端发送AJAX请求

<html>
<head>
<script type="text/javascript">
  function loadXMLDoc() {
  var xmlhttp;
  if (window.XMLHttpRequest){
    // code for IE7+, Firefox, Chrome, Opera, Safari
    xmlhttp = new XMLHttpRequest();
  } else {
    // code for IE6, IE5
    xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
  }
  xmlhttp.onreadystatechange = function() {
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
      document.getElementById("myDiv").innerHTML = xmlhttp.responseText;
      }
  }
  xmlhttp.open("GET","http://api.devgou.com/user",true);
  xmlhttp.send();
  }
</script>
</head>
<body>

<h2>AJAX CORS</h2>
<button type="button" onclick="loadXMLDoc()">请求数据</button>
<div id="myDiv"></div>

</body>
</html>

这跟一次正常的异步 ajax 请求没有什么区别,关键是在服务端收到请求后的处理:
关键是在于后端设置响应头中的 Access-Control-Allow-Origin,该值要与请求头中 Origin 一致才能生效,否则将跨域失败。

关于跨域,你想知道的全在这里
https://zhuanlan.zhihu.com/p/25778815

nginx代理配置

比如服务端是Spring后台接口,用nginx做请求转发

需在 nginx 中配置的几个参数:
Access-Control-Allow-Origin:必含,允许的域名,只能填通配符或者单域名
Access-Control-Allow-Methods:必含,允许跨域请求的 http 方法
Access-Control-Allow-Headers:返回支持的 http 请求头
Access-Control-Allow-Credentials:可选,标志着当前请求是否包含 cookies 信息,布尔值。只有一个可选值:true,如果不包含 cookies,请略去该项,而不是填 false。这一项与XmlHttpRequest 对象当中的 withCredentials 属性应保持一致,即 withCredentials 为true时该项也为 true;withCredentials为false时,省略该项不写。反之则导致请求失败。
Access-Control-Max-Age:可选,以秒为单位的缓存时间,用于缓存预检请求。

如果只响应简单请求,且不使用cookie,只设置Access-Control-Allow-Origin为前端的域名即可
比如只允许 http://devgou.com 访问,可如下配置:

add_header 'Access-Control-Allow-Origin' 'http://devgou.com';

如果需要允许来自任何域的访问,可以这样配置

add_header Access-Control-Allow-Origin *;

nginx多域名CORS配置

如果不想允许所有,但是又需要允许多个域名,那么就需要用到nginx的 map 指令
比如想允许如下map中的四个域名访问:

http {
    map $http_origin $corsHost {
        default 0;
        "~http://madaimeng.com" http://madaimeng.com;
        "~http://devgou.com" http://devgou.com;
        "~http://masikkk.com" http://masikkk.com;
        "~http://localhost:4000" http://localhost:4000;
    }

    server {
        listen       80;
        server_name  api.masikkk.com api.madaimeng.com api.devgou.com;

        location / {
          add_header 'Access-Control-Allow-Origin' $corsHost;
          proxy_pass http://localhost:8001;
        }
    }
}

nginx CORS 的配置
https://blog.csdn.net/hukfei/article/details/72832428

nginx通过CORS实现跨域
https://www.cnblogs.com/sunmmi/articles/5956554.html


CORS优缺点

优点
CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
支持所有类型的 HTTP 请求。

缺点
存在兼容性问题,特别是 IE10 以下的浏览器。
第一次发送非简单请求时会多一次请求。

九种跨域方式实现原理(完整版)
https://www.jianshu.com/p/cedc8b1cd84c


上一篇 领域驱动设计

下一篇 Hexo博客(24)VPS中部署Hexo

阅读
评论
3,146
阅读预计12分钟
创建日期 2019-02-17
修改日期 2019-02-20
类别
标签

页面信息

location:
protocol:
host:
hostname:
origin:
pathname:
href:
document:
referrer:
navigator:
platform:
userAgent:

评论