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

一、什么是跨域?

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

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

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

  • 无法读取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的原理是利用 <script>标签没有跨域限制,通过<script>中的src属性,发送带有callback参数get请求,服务端将接口返回拼凑到callback函数中,返回给浏览器,浏览器解析执行,从而前端拿到callback函数返回的数据。

jsonp解决跨域问题的缺点是: 只能发送get一种请求。

  • 原生js实现
     <script>
        var script = document.createElement('script');
        script.type = 'text/javascript';
     
        // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
        script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
        document.head.appendChild(script);
     
        // 回调执行函数
        function handleCallback(res) {
            alert(JSON.stringify(res));
        }
     </script>

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

    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-HeadersCORS请求时,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以上
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值