前端解决跨域问题的常见方法及优缺点对比

一、什么是跨域?

在前端领域中,跨域是指浏览器语序向服务器发送跨域请求,从而克服Ajax只能同源使用的限制。

什么是同源策略?

同源策略是一种约定,由Netscape公司1995年引入浏览器,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,流览器很容易受到XSS、CSFR等攻击。所谓同源就是指**“协议+域名+端口”三者相同**,即使两个不同的域名指向同一ip地址,也非同源。

同源策略限制为以下几种行为:

  • Cookie、LocalStorage和IndexDB无法获取。
  • DOM和JS对象无法获得
  • AJAX请求不能发送

二、常见的跨域场景

在这里插入图片描述

三、跨域问题常见解决方案

1.JSONP跨域

(一)原理

jsonp的原理就是:利用

(二)示例

前端代码:

1)原生JS实现

<script>
    var script = document.createElement('script');//创建一个标签名为script的元素
    script.type = 'text/javascript';//type 属性规定脚本的 MIME 类型

    // 传参一个回调函数名给后端,方便后端返回时执行这个在前端定义的回调函数
    script.src = 'http://www.domain2.com:8080/login?user=admin&callback=handleCallback';
    document.head.appendChild(script);//appendChild() 方法向节点添加最后一个子节点。

    // 回调执行函数
    function handleCallback(res) {
        alert(JSON.stringify(res));//将javaScript对象转换称JSON格式的字符串
    }
 </script>
后端代码:
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]);
//split()表示分割字符串    //querystring.parse()querystring.parse()方法用于将URL查询字符串解析为包含查询URL的键和对值的对象。
    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...');

(三)JSONP的优缺点

优点:简单,兼容性好,可用于解决主流浏览器的跨域数据访问的问题
缺点:仅支持get方法,具有局限性,不安全

2. 跨域资源共享(CORS)

(一)什么是CORS

CORS是一个W3C标准,全称是”跨域资源共享”允许浏览器向跨域服务器,发出XMLHttpRequest请求,从而克服了AJAX只能同源使用的限制。
它通过服务器增加一个特殊的Header[Access-Control-Allow-Origin]来告诉客户端跨域的限制,如果浏览器支持CORS,就会允许XMLHttpRequest发起跨域请求。

(二)简单请求和非简单请求

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

  1. 使用下列方法之一:
  • head
  • get
  • post
  1. 请求的Heder是
  • Accept
  • Accept-Language
  • Content-Language
  • Content-Type:
    只限于三个值:application/x-www-form-urlencoded、multipart/form-data、text/plain

不同时满足上面的两个条件,就属于非简单请求。浏览器对这两种的处理,是不一样的。

简单请求

对于简单请求,浏览器直接发出CORS请求。具体来说,就是在头信息之中,增加一个Origin字段。

GET /cors HTTP/1.1
Origin: http://api.bob.com
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0...

上面的头信息中,Origin字段用来说明,本次请求来自哪个源(协议 + 域名 + 端口)。服务器根据这个值,决定是否同意这次请求。

CORS请求设置的响应头字段,都以 Access-Control-开头

1)Access-Control-Allow-Origin:必选

它的值要么是请求时Origin字段的值,要么是一个*,表示接受任意域名的请求。

2)Access-Control-Allow-Credentials:可选

它的值是一个布尔值,表示是否允许发送Cookie。默认情况下,Cookie不包括在CORS请求之中。设为true,即表示服务器明确许可,Cookie可以包含在请求中,一起发给服务器。
  这个值也只能设为true,如果服务器不要浏览器发送Cookie,删除该字段即可。

3)Access-Control-Expose-Headers:可选

CORS请求时,XMLHttpRequest对象的getResponseHeader()方法只能拿到6个基本字段:Cache-Control、Content-Language、Content-Type、Expires、Last-Modified、Pragma。如果想拿到其他字段,就必须在Access-Control-Expose-Headers里面指定。上面的例子指定,getResponseHeader(‘FooBar’)可以返回FooBar字段的值。

非简单请求

非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求的CORS请求,会在正式通信之前,增加一次HTTP查询请求,称为"预检"请求(preflight)。

预检请求

预检"请求用的请求方法是OPTIONS,表示这个请求是用来询问的。请求头信息里面,关键字段是Origin,表示请求来自哪个源。除了Origin字段,"预检"请求的头信息包括两个特殊字段。

OPTIONS /cors HTTP/1.1
Origin: http://api.bob.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: X-Custom-Header
Host: api.alice.com
Accept-Language: en-US
Connection: keep-alive
User-Agent: Mozilla/5.0..

1)Access-Control-Request-Method:必选

用来列出浏览器的CORS请求会用到哪些HTTP方法,上例是PUT。

2)Access-Control-Request-Headers:可选

该字段是一个逗号分隔的字符串,指定浏览器CORS请求会额外发送的头信息字段,上例是X-Custom-Header。

预检请求的回应

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

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

它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求。

2)Access-Control-Allow-Headers

如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段。

3)Access-Control-Allow-Credentials:可选

该字段与简单请求时的含义相同。

4)Access-Control-Max-Age:可选
  用来指定本次预检请求的有效期,单位为秒。

(三)示例

前端代码:
var xhr = new XMLHttpRequest(); // 创建一个XMLHttpRequest对象,IE8/9需用window.XDomainRequest兼容

// 前端设置是否带cookie
xhr.withCredentials = true;

//建立http连接
xhr.open('post', 'http://www.domain2.com:8080/login', true);//true表示是否异步执行操作,。如果值为false,send()方法直到收到答复前不会返回。如果true,已完成事务的通知可供事件监听器使用。如果multipart属性为true则这个必须为true,否则将引发异常。默认为true

//设置http请求头部
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');

//发送请求
xhr.send('user=admin');

//获取返回的数据
//就等待远程主机做出回应。这时需要监控XMLHttpRequest对象的状态变化,指定回调函数 
xhr.onreadystatechange = function() {
    if (xhr.readyState == 4 && xhr.status == 200) {
        alert(xhr.responseText);//获取从后端返回的文本
    }
};

在这里插入图片描述

后端代码:
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));//将postData对象转换为JSON格式的字符串
        res.end();
    });
});

server.listen('8080');
console.log('Server is running at port 8080...');

(四)CORS的优缺点

优点:CORS支持所有类型的HTTP请求,是跨域HTTP请求的根本解决方案
缺点:对浏览器要求较高,低版本浏览器无法兼容

3.nodejs中间件代理跨域

(一)原理

在这里插入图片描述

同源策略是浏览器需要遵循的标准,而如果是服务器向服务器请求则无需遵循同源策略。

  • 代理服务器接受客户端请求 。
  • 代理服务器将请求 转发给服务器。
  • 代理服务器拿到服务器 响应 数据。
  • 代理服务器将 响应 转发给客户端。

(二)示例

使用node + express + http-proxy-middleware搭建一个proxy服务器。

前端代码:
//创建一个XMLHttpRequest对象
var xhr = new XMLHttpRequest();

// 前端开关:浏览器是否读写cookie
xhr.withCredentials = true;

// 访问http-proxy-middleware代理服务器
//建立http连接
xhr.open('get', 'http://www.domain1.com:3000/login?user=admin', true);//地址是代理服务器的地址
//发送请求
xhr.send();
中间件服务器代码:
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...');

(三)nodejs中间件代理的优缺点

优点:减少网络负荷,节约带宽,实现自动数据分流
缺点:易受攻击

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值