搞懂:前端跨域问题JS解决跨域问题VUE代理解决跨域问题原理

什么是跨域

跨域一个域下的文档或脚本试图去请求另外一个域下的资源javascript

广义的跨域包含一下内容html

1.资源跳转(连接跳转,重定向跳转,表单提交)
2.资源请求(内部的引用,脚本script,图片img,frame)
3.script内部发起的请求(ajax,dom请求,和js跨域调用

跨域问题出现前端

只有浏览器端出现,直接用终端请求,是不会出现跨域拦截,是属于浏览器端的安全策略,浏览器将不一样源的请求进行了拦截,限制了跨域资源访问

什么是同源vue

同源策略:same origin policy,若是两个资源(页面)`协议`,`域名`,`端口`都相同,那么就是同源。

即便:两个不一样域名指向同一个ip,也算非同源

非同源(跨域)有哪些限制java

* cookie,localstorage,indexDB没法读取
* Dom和JS对象没法互通
* Ajax请求不能发送

常见的跨域demonode

URL                                      说明                    是否容许通讯
http://www.domain.com/a.js
http://www.domain.com/b.js         同一域名,不一样文件或路径           容许
http://www.domain.com/lab/c.js

http://www.domain.com:8000/a.js
http://www.domain.com/b.js         同一域名,不一样端口                不容许
 
http://www.domain.com/a.js
https://www.domain.com/b.js        同一域名,不一样协议                不容许
 
http://www.domain.com/a.js
http://192.168.4.12/b.js           域名和域名对应相同ip              不容许
 
http://www.domain.com/a.js
http://x.domain.com/b.js           主域相同,子域不一样                不容许
http://domain.com/c.js
 
http://www.domain1.com/a.js
http://www.domain2.com/b.js        不一样域名                         不容许

前端解决跨域的方法

  • JSONPwebpack

    • 原理:因为在一个页面内部js若是要访问另外一个非同源域的数据是被浏览器限制的,可是浏览器在解析和加载script标签的时候,是容许一个页面加载多个域的js标签,而js标签其实又是相似一种get请求的方式,只是返回的数据是一个JSON格式的脚本对象。
    • 用法:将get请求封装到script标签中,利用script标签作get请求,最后解析script标签数据
    • 代码:
    var  script = document.createElement('script')
      script.style = 'text/javascript'
    
      //将要请求的get地址传到script的src属性,而且添加回调函数
      script.setAttribute('scr','http://www.domain2.com:8080/login?user=admin&callback=handleCallback')
      //将script标签放入文档
      document.head.appendChild(script)
      //执行回调函数
      function handleCallback(res){
          //处理JSON格式数据
          alert(JSON.stringify(res))
      }
    • 因为请求script只能是get方法,因此JSONP这种方式只能解决get请求,post或者其余http方法没法解决
  • 跨域资源共享ios

    • 原理:
      • cors:cross-origin -resouce sharing跨域资源共享,容许浏览器向跨源服务器发出http请求,须要浏览器和服务器同时支持
      • 浏览器在发出跨域请求时:1。简单请求,直接在头部信息中带上origin信息,标识属于哪一个源,服务端配置access-control-allow-origin接受哪些域名跨域访问,能够是*容许全部
      • 若是是非简单请求会在正式请求以前,发送预检请求
    • 简单来讲(简单请求):
      • 是一个双端配合,容许打卡跨域资源限制的一种手段
      • 浏览器端:请求头携带origin信息
      • 服务端:配置access-control-allow-origin容许哪些源跨域
    • 代码:
      • 原生:xmlHttpRequest不须要配置请求头,简单请求会自动带上origin属性
        var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
        
        
            xhr.open('post', 'http://www.domain2.com:8080/login', true);
            xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
            xhr.send('user=admin');
        
            xhr.onreadystatechange = function() {
                if (xhr.readyState == 4 && xhr.status == 200) {
                    alert(xhr.responseText);
                }
            };
      • AJAX:有一个属性crossDomain,配置为true以后会让请求头携带orgin跨域额外信息,可是不会自动包含cookie
        $.ajax({
            ...
        xhrFields: {
            withCredentials: true    // 前端设置是否带cookie
        },
        crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
            ...
        });
      • VUE中的axios,默认也会把origin放到请求头,不须要额外配置
    • 服务端配置
      /*
         * 导入包:import javax.servlet.http.HttpServletResponse;
         * 接口参数中定义:HttpServletResponse response
         */
      
        // 容许跨域访问的域名:如有端口需写全(协议+域名+端口),若没有端口末尾不用加'/'
        response.setHeader("Access-Control-Allow-Origin", "http://www.domain1.com"); 
      
        // 容许前端带认证cookie:启用此项后,上面的域名不能为'*',必须指定具体的域名,不然浏览器会提示
        response.setHeader("Access-Control-Allow-Credentials", "true"); 
      
        // 提示OPTIONS预检时,后端须要设置的两个经常使用自定义头
        response.setHeader("Access-Control-Allow-Headers", "Content-Type,X-Requested-With");
  • 反向代理(NodeJs中间件代理跨域)(Vue代理跨域)(nginx转发代理)nginx

    • 场景:当服务器端没法修改cors,而且client所有换成jsonp方式比较繁琐时,只能经过中间代理服务器,将代理服务器设置为支持CORS或者代理服务器和请求页面同源,使页面能够直接请求代理服务器,在经过代理服务器进行接口代理,代理请求目标接口地址,返回数据web

    • 原理:跨域限制仅是浏览器端的安全策略,并非http协议的固有限制,因此中间服务器在参数和cookie有效的状况下是能够正常的请求目标服务器的,Vue(node+webpack+webpack-dev-server)中配置proxy就是启动一个同源的服务进行接口代理

    • 注意:

      • 1.非vue ,webpack,dev-serve服务,单独启的nginx或者node express服务作反向代理时,通常是跟页面非同源,非同源要访问代理服务器也存在跨域问题,须要配置cors容许跨域访问,配置access-control-allow-header
      • 在webpack-dev-server服务,默认应该是基本与测试环境页面同源,不须要配置请求头容许
    • 代码:

      • webpack-dev-server代理
      module.exports = {
              entry: {},
              module: {},
              ...
              devServer: {
                  historyApiFallback: true,
                  proxy: [{
                      context: '/login',
                      target: 'http://www.domain2.com:8080',  // 代理跨域目标接口
                      changeOrigin: true,
                      secure: false,  // 当代理某些https服务报错时用
                      cookieDomainRewrite: 'www.domain1.com'  // 能够为false,表示不修改
                  }],
                  noInfo: true
              }
          }
      • node express反向代理
      var express = require('express');
      var proxy = require('http-proxy-middleware');
      var app = express();
      
      app.use('/', proxy({
          // 代理跨域目标接口
          target: 'http://www.domain2.com:8080',
          changeOrigin: true,
      
          // 修改响应头信息,实现跨域并容许带cookie
          onProxyRes: function(proxyRes, req, res) {
              res.header('Access-Control-Allow-Origin', 'http://www.domain1.com');
              res.header('Access-Control-Allow-Credentials', 'true');
          },
      
          // 修改响应信息中的cookie域名
          cookieDomainRewrite: 'www.domain1.com'  // 能够为false,表示不修改
      }));
      
      app.listen(3000);
      console.log('Proxy server is listen at port 3000...');
      • nginx转发代理
      #proxy服务器
          server {
              listen       81;
              server_name  www.domain1.com;
      
              location / {
                  proxy_pass   http://www.domain2.com:8080;  #反向代理
                  proxy_cookie_domain www.domain2.com www.domain1.com; #修改cookie里域名
                  index  index.html index.htm;
      
                  # 当用webpack-dev-server等中间件代理接口访问nignx时,此时无浏览器参与,故没有同源限制,下面的跨域配置可不启用
                  add_header Access-Control-Allow-Origin http://www.domain1.com;  #当前端只跨域不带cookie时,可为*
                  add_header Access-Control-Allow-Credentials true;
              }
          }
  • 其余js HACK方法

    • 主域相同子域不一样场景:docment.domain+iframe

    • 三个页面跨域互传 : location.hash + iframe

    • window.name + iframe

    • H5新增的postMessage(跨窗口消息互通方法)

    • 关于iframe:

      • 其实至关于在当前页面下新开了个子页面。遵循一切页面与页面之间资源访问原则,例如
        • 1.当主页面和iframe页面不一样源时,没法互相访问DOM
        • 2.当主域同样,二级域名不一样时,document.domain的设置,能够规避同源策略,能够互相访问DOM
    • location.hash:

      • url 末尾#后面跟随的时页面片断标识符,用来表示浏览器渲染哪部分页面信息,可是改变这个值页面不会从新刷新, 而且设置以后,全部页面能够经过document对象访问该窗口的location 信息进行获取,能够实现巧妙跨域
      • 代码:
      //父窗口能够把信息,写入子窗口的片断标识符。
      
          var src = originURL + ‘#’ + data;
          document.getElementById(‘myIFrame’).src = src;
      
          //子窗口经过监听hashchange事件获得通知。
      
          window.onhashchange = checkMessage;
      
          function checkMessage() {
          var message = window.location.hash;
          // …
          }
    • 同理,window.name也是一种hack方法实现跨域的方式

      • 浏览器窗口有window.name属性。这个属性的最大特色是,不管是否同源,只要在同一个窗口里,前一个网页设置了这个属性,后一个网页能够读取它。

        父窗口先打开一个子窗口,载入一个不一样源的网页,该网页将信息写入window.name属性。

        window.name = data;

        接着,子窗口跳回一个与主窗口同域的网址。

        location = ‘http://parent.url.com/xxx.html’;

        而后,主窗口就能够读取子窗口的window.name了。

        var data = document.getElementById(‘myFrame’).contentWindow.name;

        这种方法的优势是,window.name容量很大,能够放置很是长的字符串;缺点是必须监听子窗口window.name属性的变化,影响网页性能。

    • postMessage

      • window.name和location.hash都属于取巧的方式,利用了浏览器页面的非数据属性或者是窗口的固有属性达到跨页面的目的,可是在H5新增了postMessage API,实现跨tab跨窗口的数据通讯,容许窗口通讯,而且不管是否同源

总结:

经常使用的,仍是代理,还有CORS,或者是JSONP这三种方式,禁止跨域原本就是浏览器的一种安全策略,虽然有必定限制开发者的手脚,可是在一些特殊的网站或者安全性要求比较高的网站,网络安全仍是不可忽视的

很是感谢:下面的文章给了我不少的帮助,感谢各位前行者的辛苦付出,能够点击查阅更多信息

前端常看法决跨域问题方案

9种常见的跨域解决方案

前端跨域问题