什么是跨域?前端如何解决跨域问题?

目录

一、什么是跨域?

二、常见的跨域

三、解决跨域问题

1.jsonp方法

2.CORS(跨域资源共享)方法

3.nginx代理跨域

4.nodejs中间件代理跨域



一、什么是跨域?

浏览器从一个域名的网页去请求另一个域名的资源时,域名、端口、协议任一不同,都是跨域。

前后端分离的模式下,前后端的域名是不一致的,所以就会出现跨域问题。
而浏览器为了安全问题一般都会限制跨域访问,也就是不允许跨域请求资源。所以出现了浏览器的同源策略。

同源策略:指协议、域名、端口都要相同,其一不同都会出现跨域。
同源策略限制的几种行为:

  • 无法读取Cookie、LocalStorage和IndexDB*(IndexDB是浏览器提供的本地数据库,可以被网页脚本创建和操作,允许存贮大量数据,提供查找接口,能建立索引。)*
  • 无法获得DOM和JS对象
  • 不能发送AJAX请求

二、常见的跨域

url 说明 是否允许通信
http://www.a.com/a.js
http://www.a.com/b.js
同一域名下 允许
http://www.a.com/a/a.js
http://www.a.com/b/b.js
同一域名下不同文件夹 允许
http://www.a.com:8000/a.js
http://www.a.com/b.js
同一域名下不同端口 不允许
http://www.a.com/a.js
http://192.168.10.11/b.js
域名和该域名对应ip 不允许
http://www.a.com/a.js
http://index.a.com/b.js
http://a.com/c.js
主域相同,子域不同 不允许
http://www.a.com/a.js
http://www.b.com/b.js
不同域名 不允许
http://www.a.com/a.js
https://www.a.com/b.js
同域名,不同协议 不允许

三、解决跨域问题

1.jsonp方法

jsonp的原理是利用

服务端返回如下(返回时即执行全局函数)

handleCallback({"success": true, "user": "admin"})
  • vue的axios实现
    this.$http = axios;
    this.$http.jsonp('http://www.domain2.com:8080/login', {
        params: {},
        jsonp: 'handleCallback'
    }).then((res) => {
        console.log(res); 
    })
  •  后端node.js代码

    var querystring = require('querystring');
    var http = require('http');
    var server = http.createServer();
     
    server.on('request', function(req, res) {
        var params = querystring.parse(req.url.split('?')[1]);
        var fn = params.callback;
     
        // jsonp返回设置
        res.writeHead(200, { 'Content-Type': 'text/javascript' });
        res.write(fn + '(' + JSON.stringify(params) + ')');
     
        res.end();
    });
     
    server.listen('8080');
    console.log('Server is running at port 8080...');
    • jquery ajax实现
      $.ajax({
          url: 'http://www.domain2.com:8080/login',
          type: 'get',
          dataType: 'jsonp',  // 请求方式为jsonp
          jsonpCallback: "handleCallback",  // 自定义回调函数名
          data: {}
      });

    2.CORS(跨域资源共享)方法

    CORS(Cross-origin resource sharing)是一个W3C标准,它允许浏览器向跨源服务器,发出XMLHttpRequest请求,从而克服了ajax的同源策略。CORS需要浏览器和服务器同时支持。目前,CORS在现代浏览器都支持(IE不能低于10)。

    它有以下几个特点:

    1. CORS通信过程是由浏览器自己完成的,不需要用户参与。
    2. 对于开发者来说CORS通信和同源的ajax通信是一样的,代码也完全一致。
    3. 在使用CORS通信的过程中,一旦发起了ajax跨域请求,会自动添加一些附加的头信息,有时候还会多附加一次请求,用户不会感知到。
    4. 实现CORS的关键在于服务器,只要服务器实现了,就可以进行CORS通信。

    浏览器将cors跨域请求分为简单请求非简单请求

    • 简单请求:一次请求
    • 非简单请求:两次请求,在发送数据之前会发一次请求用于“预检”,只有预检通过了才再发送第二次请求用于数据传输。

    只要同时满足以下两个条件,就属于简单请求。

    (1)请求方法是以下三种方法之一:(如果请求方法是put / deletde等肯定是非简单请求)

    •     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,(如果你发送的application/json格式的数据,那么肯定是非简单请求,vue的axios默认的请求体信息格式是json的,ajax默认是urlencoded的。)
       

    简单请求:服务器设置响应头:Access-Control-Allow-Origin = ‘域名’ 或 ‘*’   (必选)

    cors请求设置的响应头字段:

    字段 解释 是否必选
    Access-Control-Allow-Origin 它的值是请求时Origin字段的值或‘ * ’,‘ * ’表示接受任意域名的请求 必选
    Access-Control-Allow-Credentials 它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求中。 可选
    Access-Control-Control-Expose-Headers CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段。如果想拿到其他字段,就必须在这个字段里面指定。 可选


    非简单请求:

    非简单请求时,首先发送“预检”请求,如果“预检成功”,则发送真实数据。

    1.预检请求

    允许请求方式则需服务器设置响应头:Access-Control-Request-Method (必选)

    response.addHeader("Access-Control-Allow-Methods", "POST,GET,PUT,DELETE");
    

    允许请求头则需设置响应头:​​​​​​​Access-Control-Request-Headers (可选)

     response.addHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With,
                        Content-Type,Accept,jssesionId,appId,sign,nonceStr");
    

     2.预检请求的回应

    服务器收到“预检”请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。

    HTTP 回应中,除了关键的是Access-Control-Allow-Origin字段,其他CORS相关字段如下:

    字段 解释 是否必选
    Access-Control-Allow-Methods 它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。 必选
    Access-Control-Allow-Headers 如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。 -
    Access-Control-Allow-Credentials 它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求中。(与简单请求含义相同) 可选
    Access-Control-Max-Age

    用来指定本次预检请求的有效期,单位:秒

    可选

    CORS跨域实例:

    前端设置:

    • 原生ajax:
      var xhr = new XMLHttpRequest(); // IE8/9需用window.XDomainRequest兼容
       
      // 前端设置是否带cookie
      xhr.withCredentials = true;
       
      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);
          }
      };
    • jQuery的ajax:
      $.ajax({
          ...
         xhrFields: {
             withCredentials: true    // 前端设置是否带cookie
         },
         crossDomain: true,   // 会让请求头中包含跨域的额外信息,但不会含cookie
          ...
      });

    服务端设置: 

    • nodejs代码
    var http = require('http');
    var server = http.createServer();
    var qs = require('querystring');
     
    server.on('request', function(req, res) {
        var postData = '';
     
        // 数据块接收中
        req.addListener('data', function(chunk) {
            postData += chunk;
        });
     
        // 数据接收完毕
        req.addListener('end', function() {
            postData = qs.parse(postData);
     
            // 跨域后台设置
            res.writeHead(200, {
                'Access-Control-Allow-Credentials': 'true',     // 后端允许发送Cookie
                'Access-Control-Allow-Origin': 'http://www.domain1.com',    // 允许访问的域(协议+域名+端口)
                /* 
                 * 此处设置的cookie还是domain2的而非domain1,因为后端也不能跨域写cookie(nginx反向代理可以实现),
                 * 但只要domain2中写入一次cookie认证,后面的跨域接口都能从domain2中获取cookie,从而实现所有的接口都能跨域访问
                 */
                'Set-Cookie': 'l=a123456;Path=/;Domain=www.domain2.com;HttpOnly'  // HttpOnly的作用是让js无法读取cookie
            });
     
            res.write(JSON.stringify(postData));
            res.end();
        });
    });
     
    server.listen('8080');
    console.log('Server is running at port 8080...');

    按照我理解的改了点,其他解决跨域方法看链接↓9种常见的前端跨域解决方案(详解)_他夏了夏天吖的博客-CSDN博客_前端跨域解决方案

    四、总结

    1. jsonp(只支持get请求,支持老的IE浏览器),适合加载不同域名的js、css、img等静态资源
    2. CORS(支持所有类型的HTTP请求,但不支持IE10以下的),适合做ajax各种跨域请求
    3. Nginx代理跨域和nodejs中间件跨域原理相似,都是搭建一个服务器,直接在服务器端请求HTTP接口,适合前后端分离的前端项目调后端接口。
    4. document.domain+iframe适合主域名相同,子域名不同的跨域请求。
    5. postMessage、websocket都是HTML5新特性,兼容性不是很好,只适用于主流浏览器和IE10以上

    你可能感兴趣的:(前端,前端,javascript,服务器)