乐趣区

关于跨域:深入解析各种解决跨域的方法

同源策略
同源策略,它是由 Netscape 提出的一个驰名的安全策略。当初所有反对 JavaScript 的浏览器都会应用这个策略来对脚本和申请进行校验,若不同源,则禁止应用。

同源的定义
那如果判断是否同源?次要依据三个维度,域名,协定,端口三个都雷同才算同源。
举个:

网站 A 网站 B 后果
http://www.zhenai.comhttp://i… 不同源,域名不同 http://www.zhenai.comhttp://w… 不同源,域名不同 http://www.zhenai.comhttps://… 不同源,协定不同 http://www.zhenai.comhttp://w… 不同源,端口不同(默认端口 80)

同源策略的作用
①无奈用 js 读取非同源的 Cookie、LocalStorage 和 IndexDB
这个次要是为了避免歹意网站通过 js 获取用户其余网站的 cookie 等用户信息。

②无奈用 js 获取非同源的 DOM
避免歹意网站通过 iframe 获取页面 dom,从而窃取页面的信息。

③无奈用 js 发送非同源的 AJAX 申请
避免歹意的申请攻打服务器窃取数据信息。

那是不是说非同源的申请就无奈实现呢?也不是,这就引出了咱们本文次要论述的解决跨域申请问题的办法。

jsonp
jsonp 能实现跨域是利用了 img、script 和 link 标签本身的跨域能力。
咱们晓得当 img 或者 script 中的 src 是一个链接的时候,浏览器会申请这个链接获取资源,那么这个链接如果是跨域的,浏览器也会申请,从而达到了跨域申请的一个性能。

用法

var script = document.createElement('script');
script.src = 'http://localhost:3000/api/test.do?a=1&b=2&callback=cb';
$('body').append(script);

function cb(res){
    // do something
    console.log(res)
}

能够看到,咱们创立一个 script 标签,将 src 改成咱们要申请的接口,并将 script 增加在 body 中,那么当浏览器解析到这个 script 时,会想 src 对应的服务器发送一个 get 申请,并将参数带过来。
而后当浏览器接管到服务端返回的数据,就会触发参数中 callbak 对应的回调函数 cb,从而实现整个 get 申请。

长处
简略粗犷

毛病
①只反对 get 申请
②须要后盾配合,将返回后果包装成 callback(res) 的模式

防备
那如果黑客植入 script 脚本通过 jsonp 的形式对服务器进行攻打,怎么办?
能够通过页面设置的内容平安协定 csp 进行防备。

cors 跨域
cors 是一个 W3C 规范, 全称是 ” 跨域资源共享 ”(Cross-origin resource sharing),它容许浏览器向跨源服务器发送 XMLHttpRequest 申请,从而克服了 AJAX 只能同源应用的限度
cors 须要浏览器和服务器同时反对,整个 CORS 通信过程,都是浏览器主动实现不须要用户参加,对于开发者来说,cors 的代码和失常的 ajax 没有什么差异,浏览器一旦发现跨域申请,就会增加一些附加的头信息
然而,cors 不反对 ie10 及以下版本。

简略申请和简单申请
浏览器将 cors 申请分为简略申请和简单申请。
简略申请则间接发送申请到服务器,只申请一次。
而简单申请在正式申请前都会有预检申请,在浏览器中都能看到有 OPTIONS 申请,用于向服务器申请权限信息的,须要申请两次。

那如何辨别是简略申请还是简单申请呢?

简略申请
简略申请必须要同时满足上面三个条件:

申请形式只能是:GET、POST、HEAD
HTTP 申请头限度这几种字段:Accept、Accept-Language、Content-Language、Content-Type、Last-Event-ID
Content-type 只能取:application/x-www-form-urlencoded、multipart/form-data、text/plain
content-type 的类型
类型形容 application/json 音讯主体是序列化后的 JSON 字符串 application/x-www-form-urlencoded 数据被编码为键值对。这是规范的编码格局 multipart/form-data 须要在表单中进行文件上传时,就须要应用该格局。常见的媒体格式是上传文件之时应用的 text/plain 数据以纯文本模式 (text/json/xml/html) 进行编码,其中不含任何控件或格局字符

application/json:

作用:通知服务器申请的主题内容是 json 格局的字符串,服务器端会对 json 字符串进行解析,
益处:前端人员不须要关怀数据结构的复杂度,只有是规范的 json 格局就能提交胜利。
application/x-www-form-urlencoded:是 Jquery 的 Ajax 申请默认形式

作用:在申请发送过程中会对数据进行序列化解决,以键值对模式?key1=value1&key2=value2 的形式发送到服务器。
益处:所有浏览器都反对。
简单申请
不满足简略申请的条件,那么就是简单申请。
简单申请会在正式申请发送之前,先发一个预检申请进行校验,校验通过后能力进行正式申请。
举个
浏览器当初要发送一个 put 的简单申请,那么在 put 申请发送之前,浏览器先发送一个 options 申请。
options 申请头信息:

OPTIONS /cors HTTP/1.1
Origin: localhost:3000
Access-Control-Request-Method: PUT // 示意应用的什么 HTTP 申请办法
Access-Control-Request-Headers: X-Custom-Header // 示意浏览器发送的自定义字段
Host: localhost:3000
Accept-Language: zh-CN,zh;q=0.9
Connection: keep-alive
User-Agent: Mozilla/5.0…
服务器收到 options 申请当前,查看了 Origin、Access-Control-Request-Method 和 Access-Control-Request-Headers 字段当前,确认容许跨源申请,就能够做出回应
options 响应头信息

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://localhost:3000 // 示意 http://localhost:3000 能够拜访数据
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: X-Custom-Header
Content-Type: text/html; charset=utf-8
Content-Encoding: gzip
Content-Length: 0
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive
Content-Type: text/plain
当 options 申请通过之后收回正式的 HTTP 申请,假使 options 申请不通过,则服务器不容许此次拜访, 从而抛出谬误

options 申请通过之后的, 浏览器收回发申请

PUT /cors HTTP/1.1
Origin: http://api.zhenai.com
Host: api.alice.com
X-Custom-Header: value
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0…
options 申请缓存
那这样的话,如果页面存在大量的简单申请,岂不是每个申请后面都要进行一次 options 的申请,那不会造成大量资源的节约么?
如果基于 cors 申请的办法来解决跨域问题,那么简单申请之前是须要进行一个 options 的申请的,但咱们能够通过对 options 申请进行缓存来加重申请的压力。

在 options 申请中,咱们能够通过设置响应头的参数 Access-Control-Max-Age 来对后果进行缓存
比方:Access-Control-Max-Age: 600 示意对 options 测验后果进行十分钟的缓存

url 变动会导致缓存生效, 须要从新验证 options 申请的返回值
预检不关怀 post data
header 变动,如果是去掉了自定义的 header 使得申请变成简略申请,不会发送 options 申请。如果是减少其余的 header,是会从新验证 Access-Control-Allow-Headers 的值。
cookie 变动,只有后端容许发送 cookie,cookie 值变动不会导致缓存生效。
该字段的兼容性如下:

nginx
nginx 解决跨域的问题跟之前的办法有所不同,它是通过服务器的方向代理,将前端拜访域名跟后端服务域名映射到同源的地址下,从而实现前端服务和后端服务的同源,那天然不存在跨域的问题了。
举个:
前端服务:http://localhost:3000,
前端页面路由:http://localhost:3000/page.html,
后端服务:http://localhost:3001,
后端接口路由:http://localhost:3001/api/test.do
能够看出,两个服务处于跨域的状态
通过 nginx 的配置进行反向代理,即可实现前后端服务同源,如下:

server
{
    listen 80;
    server_name localhost;

    location = / {proxy_pass http://localhost:3000;}

   location /api {
        proxy_pass http://localhost:3001;

        #指定容许跨域的办法,* 代表所有
        add_header Access-Control-Allow-Methods *;

        #预检命令的缓存,如果不缓存每次会发送两次申请
        add_header Access-Control-Max-Age 3600;
        #带 cookie 申请须要加上这个字段,并设置为 true
        add_header Access-Control-Allow-Credentials true;

        #示意容许这个域跨域调用(客户端发送申请的域名和端口)#$http_origin 动静获取申请客户端申请的域   不必 * 的起因是带 cookie 的申请不反对 * 号
        add_header Access-Control-Allow-Origin $http_origin;

        #示意申请头的字段 动静获取
        add_header Access-Control-Allow-Headers 
        $http_access_control_request_headers;

        #OPTIONS 预检命令,预检命令通过时才发送申请
        #查看申请的类型是不是预检命令
        if ($request_method = OPTIONS){return 200;}
   }
}

其实 nginx 不仅仅只是用于解决跨域问题,而是波及到很多服务器资源分配的解决,在此就不具体探讨了。

vue proxyTable
其实,在咱们支流应用的 MVVM 框架中,配置项外面也提供解决跨域问题的能力,持续举个,以 vue2.x 为例,咱们能够通过在 config/index.js 中增加配置项实现跨域申请:


proxyTable: {
    '/apis': {
        // 测试环境
        target: 'http://www.zhenai.com/',  // 接口域名
        changeOrigin: true,  // 是否跨域
        pathRewrite: {'^/apis': ''   // 须要 rewrite 重写的,} 
    }             
}

原理
其实原理很简略,就是在咱们应用 npm run dev 命中,启动了一个 node 服务,而后将前端收回的申请发送到 node 服务,再将该服务转发到本来的后盾服务,在这过程中实现了一层代理,由一个 node 服务发送一个申请到另外一个后盾服务,天然也没有了浏览器所限度的跨域问题。

退出移动版