跨域

2018, Jul 20    

跨域

1.同源策略

同源,就是指两个页面具有相同的协议,主机(也常说域名),端口,三个要素缺一不可

URL1 URL2 说明 是否允许通信
http://www.foo.com/js/a.js http://www.foo.com/js/b.js 协议、域名、端口都相同 允许
http://www.foo.com/js/a.js http://www.foo.com:8888/js/b.js 协议、域名相同,端口不同 不允许
https://www.foo.com/js/a.js http://www.foo.com/js/b.js 主机、域名相同,协议不同 不允许
http://www.foo.com/js/a.js http://www.bar.com/js/b.js 协议、端口相同,域名不同 不允许
http://www.foo.com/js/a.js http://foo.com/js/b.js 协议、端口相同,主域名相同,子域名不同 不允许

同源策略,同源策略限制的不同源之间的交互主要针对的是js中的XMLHttpRequest等请求

下面这些情况是完全不受同源策略限制的。

  • 页面中的链接,重定向以及表单提交是不会受到同源策略限制的。链接就不用说了,导航网站上的链接都是链接到其他站点的。而你在域名www.foo.com下面提交一个表单到www.bar.com是完全可以的。
  • 跨域资源嵌入是允许的,当然,浏览器限制了Javascript不能读写加载的内容。如前面提到的嵌入的<script src="..."></script>,<img>,<link>,<iframe>等。当然,如果要阻止iframe嵌入我们网站的资源(页面或者js等),我们可以在web服务器加上一个X-Frame-Options DENY头部来限制。nginx就可以这样设置add_header X-Frame-Options DENY;

2.跨域问题

跨域问题,就是由于浏览器的同源策略,导致出现请求跨域错误问题 。

我们来说一下常见的ajax跨域问题

ajax出现请求跨域错误问题,主要原因就是因为浏览器的“同源策略”

解决ajax跨域 的方式有三种

  • JSONP方式:利用script标签发起get请求不会出现跨域禁止的特点实现
  • window.name+iframe 借助中介属性window.name实现
  • CORS方式:需要服务器设置header:Access-Control-Allow-Origin
  • 代理请求方式:Nginx反向代理 ,服务端之间的资源请求不会有跨域限制
2.1. CORS 解决ajax跨域

CORS是一个W3C标准,全称是”跨域资源共享”(Cross-origin resource sharing)。它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。 目前几乎所有的浏览器ajax请求都是基于CORS机制。

CROS详解

浏览器将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-urlencoded、 multipart/form-data、text/plain)

凡是不同时满足上面两个条件,就属于非简单请求

浏览器对这两种请求的处理,是不一样的

简单请求
  1. 在请求中需要附加一个额外的 Origin 头部,其中包含请求页面的源信息(协议、域名和端口),以便服务器根据这个头部信息来决定是否给予响应。例如:Origin: http://www.laixiangran.cn
  2. 如果服务器认为这个请求可以接受,就在 Access-Control-Allow-Origin 头部中回发相同的源信息(如果是公共资源,可以回发 * )。例如:Access-Control-Allow-Origin:http://www.laixiangran.cn
  3. 没有这个头部或者有这个头部但源信息不匹配,浏览器就会驳回请求。正常情况下,浏览器会处理请求。注意,请求和响应都不包含 cookie 信息。
  4. 如果需要包含 cookie 信息,ajax 请求需要设置 xhr 的属性 withCredentials 为 true,服务器需要设置响应头部 Access-Control-Allow-Credentials: true
非简单请求

浏览器在发送真正的请求之前,会先发送一个 Preflight 请求给服务器,这种请求使用 OPTIONS 方法,发送下列头部:

  • Origin:与简单的请求相同。
  • Access-Control-Request-Method: 请求自身使用的方法。
  • Access-Control-Request-Headers: (可选)自定义的头部信息,多个头部以逗号分隔。

例如:

Origin: http://www.laixiangran.cn
Access-Control-Request-Method: POST
Access-Control-Request-Headers: NCZ

发送这个请求后,服务器可以决定是否允许这种类型的请求。服务器通过在响应中发送如下头部与浏览器进行沟通:

  • Access-Control-Allow-Origin:与简单的请求相同。
  • Access-Control-Allow-Methods: 允许的方法,多个方法以逗号分隔。
  • Access-Control-Allow-Headers: 允许的头部,多个方法以逗号分隔。
  • Access-Control-Max-Age: 应该将这个 Preflight 请求缓存多长时间(以秒表示)。

例如:

Access-Control-Allow-Origin: http://www.laixiangran.cn
Access-Control-Allow-Methods: GET, POST
Access-Control-Allow-Headers: NCZ
Access-Control-Max-Age: 1728000

一旦服务器通过 Preflight 请求允许该请求之后,以后每次浏览器正常的 CORS 请求,就都跟简单请求一样了。

优点
  • CORS 通信与同源的 AJAX 通信没有差别,代码完全一样,容易维护。
  • 支持所有类型的 HTTP 请求。
缺点
  • 存在兼容性问题,特别是 IE10 以下的浏览器。
  • 第一次发送非简单请求时会多一次请求。
Cross实现方案
  • JAVA后台配置
  • 添加@CrossOrigin注解
2.2 代理请求方式解决ajax跨域

niginx在反向代理的时候增加如下配置

    location /user {
      if ($request_method = 'GET') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
        #add_header Access-Control-Allow-Headers 'Accept,Origin,Referer,DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
      }

      if ($request_method = 'OPTIONS') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
        return 204;
      }

      if ($request_method = 'POST') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
      }

      if ($request_method = 'DELETE') {
        add_header Access-Control-Allow-Origin *;
        add_header Access-Control-Allow-Methods 'GET,POST,PUT,DELETE,OPTIONS';
        add_header Access-Control-Allow-Headers 'DNT,X-Mx-ReqToken,Keep-Alive,User-Agent,X-Requested-With,If-Modified-Since,Cache-Control,Content-Type,Authorization';
      }
      proxy_pass http://172.17.0.1:9092;
    }