跨域问题解决方案

1. 什么是跨域?

前端项目绝大多数运行在浏览器上,浏览器是一个开放的平台,为了保障各类域名网站的数据安全,浏览器设置了一个安全功能,就是同源策略。从而产生了我们经常绕不开的话题:跨域问题-不同源问题。
因为浏览器出于安全考虑,存在同源策略。所谓同源是指协议,域名,端口均相同,也就是说,如果协议、域名或者端口有一个不同就是跨域。

例:
http://localhost:8088/   协议:http,域名:localhost,端口号:8088 
https://www.baidu.com/      协议:https,域名:www.baidu.com,端口号:80

跨域请求是指一个域的网页去请求另一个域的资源。同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互(数据传输或通信),比如客户端与服务端跨域通信,或者跨页面通信。

cookie和token的区别

跨域的特点

  1. 无法用js读取非同源的Cookie、LocalStorage 和 IndexDB。这些都是存储用户信息的API,安全性要求较高!
  2. 无法用js获取非同源的DOM 。
  3. js可以向非同源的服务器发请求,但是服务器返回的数据会被浏览器拦截。

注意:同源策略是由浏览器来执行。所有的限制都是浏览器的作用。这是浏览器为了保护用户的数据安全而采取的策略。如果所用的浏览器没有执行同源策略,那么用户登录网站的所以信息都会被窃取。

2. 解决跨域问题的方法

1. 客户端与服务端跨域通信

(1). 图像ping(单向)
  1. 什么是图像ping:
    图像ping是与服务器进行简单、单向的跨域通信的一种方式,请求的数据是通过查询字符串的形式发送的,而响应可以是任意内容,但通常是像素图或204响应(No Content)。 通过图像ping,浏览器得不到任何服务器的响应文本,都只能通过侦听load和error事件,知道响应是什么时候接收到的。图像ping有两个主要缺点:首先就是只能发送get请求,其次就是无法访问服务器的响应文本。因此图像ping只能进行单向通信。
  2. 简单使用
    var img = new Image();
    img.onload = img.onerror = function(){
    alert("done!");
    };
    img.src = "https://www.example.com/test?name='tom'";
    
  • 与< img>类似的可以跨域内嵌资源的还有:

    (1)< script src=“”>< /script>标签嵌入跨域脚本。语法错误信息只能在同源脚本中捕捉到。
    (2) < link src=“”>标签嵌入CSS。
    (3)< video> 和 < audio>嵌入多媒体资源。
    (4)< object>, < embed> 和< applet>的插件。
    (5)@font-face引入的字体。一些浏览器允许跨域字体( cross-origin fonts),一些需要同源字体(same-origin fonts)。
    (6) < frame> 和 < iframe>载入的任何资源。站点可以使用X-Frame-Options消息头来阻止这种形式的跨域交互。

(2). 通过jsonp跨域

JSONP是JSON with Padding(填充式json)的简写,是应用JSON的一种新方法,只不过是被包含在函数调用中的JSON,例如:

callback({"name","trigkit4"});

JSONP由两部分组成:回调函数和数据。回调函数是当响应到来时应该在页面中调用的函数,而数据就是传入回调函数中的JSON数据。
【< script>标签与< img>标签都有能力不受限制的从其他域加载资源】
JSONP的原理:通过< script>标签来使用的,使用时通过src属性指定一个跨域url,在url参数中指定的回调函数,传送给服务端,然后服务端生成一段js代码且代码中包含着对回调函数的调用,然后服务端把这段js代码返回给客户端,客户端浏览器获取到这段js并能自动执行,在执行这段代码的过程中调用了回调函数,然后你就可以在回调函数里面处理返回数据了。

<script type="text/javascript">
    function dosomething(jsondata){
        //处理获得的json数据
    }
</script>
<script src="http://example.com/data.php?callback=dosomething"></script>

  • JSONP的优缺点:
  1. JSONP的优点是:它不受到同源策略的限制;它的兼容性更好,在更加古老的浏览器中都可以运行;在请求完毕后可以通过回调函数获取响应数据。
  2. JSONP的缺点则是:它只支持GET请求;它不安全,可能会遭受XSS攻击。
(3). 跨域资源共享(CORS)

CORS(Cross-Origin Resource Sharing)跨域资源共享。
CORS的基本思想就是使用HTTP头部让浏览器与服务器进行沟通,从而决定请求或响应是应该成功还是失败。

浏览器将CORS跨域请求分为简单请求和非简单请求。只要同时满足以下两个条件,就属于简单请求:
(1)使用下列方法之一:

  • head
  • get
  • post

(2)HTTP的头信息不超出以下几种字段:

  • Accept
  • Accept-Language
  • Content-Language
  • Last-Event-ID
  • 有Content-Type 但字段的类型不是application/json
  • 简单请求
    对于简单请求,客户端不需要进行处理,在发送请求时,浏览器会自动附加一个额外的Origin请求头部,其中包含请求页面的源信息(协议、域名和端口)。以便服务器根据这个头部信息来决定是否给予响应。如果服务器认为请求可以接受。就会设置Access-Control-Allow-Origin的响应头字段,来允许可以访问的域进行跨域操作。
    // *表示允许所有域访问或指定详细源信息(协议、域名和端口)
    'Access-Control-Allow-Origin: * ’

  • 非简单请求
    非简单请求是那种对服务器有特殊要求的请求,比如请求方法是PUT或DELETE,或者Content-Type字段的类型是application/json。非简单请求会在正式通信之前首先使用 OPTIONS 方法发起一个预检请求到服务器,以获知服务器是否允许该实际请求。"预检请求“的使用,可以避免跨域请求对服务器的用户数据产生未预期的影响。服务器收到"预检"请求以后,检查了Origin、Access-Control-Request-Method和Access-Control-Request-Headers字段以后,确认允许跨源请求,就可以做出回应。设置以 Access-Control-开头的一系列响应头字段,来允许跨域操作:
    'Access-Control-Allow-Origin: * ’
    'Access-Control-Allow-Methods:* ’

CORS和JSONP对比
(CORS与JSONP相比,无疑更为先进、方便和可靠。)

  1. JSONP只能实现GET请求,而CORS支持所有类型的HTTP请求。
  2. 使用CORS,开发者可以使用普通的XMLHttpRequest发起请求和获得数据,比起JSONP有更好的错误处理。
  3. JSONP主要被老的浏览器支持,它们往往不支持CORS,而绝大多数现代浏览器都已经支持了CORS)。
(4). webpack的devServer.proxy处理跨域

vue在vue.config.js(新建)配置重写webpack,修改devServer 配置proxy实现跨域。原理大致与 nginx 相同。通过配置proxy在本地虚拟一个代理服务器并代理转发请求,隐藏真实的服务器,实现数据的转发

#代理服务器
module.exports = {
  devServer:{
    host:'localhost',
    port:8080,
    proxy:{
      '/api':{
      	//真实服务器地址
        target:'http://122.51.238.153',
        //在本地虚拟一个代理服务器并代理转发请求
        changeOrigin:true,
        pathRewrite:{
        // 请求时,会进行替换
          '^/api':'' 
        }
      }
    }
  }
}
(5). Websocket(不在同源限制范围内)

Websocket 是 HTML5 的一个持久化的协议,它实现了浏览器与服务器的全双工通信,同时也是跨域的一种解决方案。

(6). nginx反向代理跨域

原理图
实现思路:通过nginx配置一个代理服务器做跳板机,反向代理要访问的另一个域的服务器,隐藏真实的服务器。
使用nginx反向代理实现跨域,是最简单的跨域方式。只需要修改nginx的配置即可解决跨域问题,支持所有浏览器,支持session,不需要修改任何代码,并且不会影响服务器性能。

先下载nginx,然后将nginx目录下的nginx.conf修改配置:

server {
	#监听80端口,不加端口号时默认为80端口
    listen       80;
    #访问域名为www.123.com
    server_name  www.123.com;

    location / {
        proxy_pass http://127.0.0.1:8080;
        # 该指令用于设置网站的默认首页。
        index  index.html index.htm index.jsp;
    }
}

最后通过命令行nginx -s reload启动nginx

nginx-proxy模块

2. 主域相同,子域不同的应用场景

设置document.domain解决无法读取非同源网页的 Cookie问题,适用于主域相同,子域不同的跨域应用场景。因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie。如a.test.com 和b.test.com 。 两个页面都需要设置:document.domain = ‘test.com’;

3. 多窗口通信的应用场景

跨文档通信 API:window.postMessage()
调用postMessage方法实现父窗口http://test1.com 向子窗口 http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)

应用场景:
   1)页面和其打开的新窗口的数据传递(同级)
   2)多窗口之间消息传递(同级)
   3)页面与嵌套的iframe消息传递(父子级)
// 父窗口打开一个子窗口
let openWindow = window.open('http://test2.com', '你好2022');
// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)
// 父窗口发送信息
openWindow.postMessage('Nice to meet you!', 'http://test2.com');
// 子窗口监听 message 消息
window.addEventListener('message', function (e) {
  console.log(e.source); // e.source 发送消息的窗口
  console.log(e.origin); // e.origin 消息发向的网址
  console.log(e.data);   // e.data   发送的消息
},false);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值