前端开发|那些年曾谈起的跨域

对于前端开发来说跨域应该是最不陌生的问题了,无论是开发过程中还是在面试过程中都是一个经常遇到的一个问题,在开发过程中遇到这个问题的话一般都是找后端同学去解决,以至于很多人都忽略了对跨域的认识。为什么会导致跨域?遇到跨域又怎么去解决呢?本文会对这些问题一一的介绍。

创新互联从2013年成立,是专业互联网技术服务公司,拥有项目成都做网站、成都网站制作网站策划,项目实施与项目整合能力。我们以让每一个梦想脱颖而出为使命,1280元麻山做网站,已为上家服务,为麻山各地企业和个人服务,联系电话:13518219792

在JavaScript中,在不同的域名下面进行数据交互,就会遇到跨域问题,说到跨域首先要从同源说起,浏览器为了提供一种安全的运行环境,各个浏览器厂商协定使用同源策略。

什么同源策略

同源策略:同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。

同源策略是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,浏览器很容易受到XSS、CSRF等攻击。所谓同源是指协议+域名+端口三者相同,即便两个不同的域名指向同一个ip地址,也非同源。

Url组成部分

了解同源策略以后,同样需要对url的组成部分也顺带了解一下吧,只有了解url之后当出现跨域的时候才知道哪里出了问题,这样才能和后端、运维开怼,怼天怼地对空气。O(∩_∩)O哈哈~

从上图中能够清晰的看出url中每个部分的含义:

  1. protocol:协议常用的协议是http
  2. auth:验证,因为明文传输用户名和密码,非HTTPS环境下很不安全,一般用的非常少
  3. hostname:主机地址,可以是域名,也可以是IP地址
  4. port:端口http协议默认端口是:80端口,如果不写默认就是:80端口
  5. pathname:路径网络资源在服务器中的指定路径
  6. serarch:查询字符串如果需要从服务器那里查询内容,在这里编辑
  7. hash:哈希网页中可能会分为不同的片段,如果想访问网页后直接到达指定位置,可以在这部分设置

项目过程过程中经常会用到一些缓存,浏览器为了网页的安全在缓存的时候,由于同源策略的问题对其缓存内容进行了限制,其实想想也是对的,如果不使用同源策略的话,很容易冲掉缓存的东西。

  1. Cookie、LocalStorage和IndexDB等无法读取。
  2. DOM无法获得。
  3. AJAX请求不能发送。

当然浏览器也没有把所有的东西都限制了,比如图片、互联网资源等还是允许跨域请求的。允许跨域请求都是使用标签,只有三个标签是允许跨域加载资源:

  1.   
  2.   
  3.   

http://localhost:7000/b.html 

 
 
 
 
  1.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9. function checkHash(){  
  10.     var data = '';  
  11.     switch(location.hash){  
  12.         case '#Aaron':  
  13.               data = 'my Aaron';  
  14.               break;  
  15.         case '#Angie':  
  16.               data = 'my Angie';  
  17.               break;  
  18.         default : break;  
  19.     }  
  20.     data && callBack('#'+data);  
  21. }  
  22. function callBack(hash){  
  23.    var proxy = document.createElement('iframe');  
  24.    proxy.style.display = 'none';  
  25.    proxy.src = 'http://localhost/c.html'+hash;  
  26.    document.body.appendChild(proxy);  
  27. }  
  28. window.onload = checkHash;  
  29.   
  30.   
  31.   

http://localhost:6000/c.html 

 
 
 
 
  1.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9. parent.parent.location.hash = self.location.hash.substring(1);  
  10.   
  11.   
  12.   

a.html中有一个隐藏的iframe,该iframe指向异域http://localhost:7000/b.html的b.html,且传递hash值给b.html`b.html获取hash值,生成data值,然后动态创建iframe,该iframe将data值传给与a.html同域的c.html 因为c.html与a.html`同域,可以传值固然也就解决了跨域问题。

window.name

window.name这个属性不是一个简单的全局属性只要在一个window下,无论url怎么变化,只要设置好了window.name,那么后续就一直都不会改变,同理,在iframe中,即使url在变化,iframe中的window.name也是一个固定的值,利用这个,我们就可以实现跨域了。

http://localhost:6000/a.html 

 
 
 
 
  1.   
  2.   

http://localhost:7000/b.html 

 
 
 
 
  1. var person = {  
  2.   name: 'Aaron',  
  3.   age: 18  
  4. }  
  5. window.name = JSON.stringify(person)  

http://localhost:6000/proxy.html 

 
 
 
 
  1.   
  2.   
  3.   
  4.   
  5. proxy  
  6.   
  7.   
  8. 这是proxy页面

      
  9.   
  10.   

在http://localhost:6000下有一个a.html,在http://localhost:7000下有一个b.html,在http://localhost:6000/a.html中创建了一个iframe标签并把地址指向了http://localhost:7000/b.html,在b.html中的window.name赋值保存了一段数据,但是现在还获取不了,因为是跨域的,所以,我们可以把src设置为当前域的http://localhost:6000/proxy.html,虽然域名改变了但是window.name是没有改变的。这样就可以拿到我们想要的数据了。

postMessage(HTML5)

可能很多不知道postMessage整个API,在HTML5中新增了postMessage方法允许来自不同源的脚本采用异步方式进行有限的通信,可以实现跨文本档、多窗口、跨域消息传递,postMessage在很多浏览器中都已经得到了良好的支持,所以可放心的使用。该方法可以通过绑定window的message事件来监听发送跨文档消息传输内容。

postMessage()方法接受两个参数

   1.  data:要传递的数据,html5规范中提到该参数可以是JavaScript的任意基本类型或可复制的对象,然而并不是所有浏览器都做到了这点儿,部分浏览器只能处理字符串参数,所以我们在传递参数的时候需要使用JSON.stringify()方法对对象参数序列化,在低版本IE中引用json2.js可以实现类似效果。

   1.  origin:字符串参数,指明目标窗口的源,协议+主机+端口号+URL,URL会被忽略,所以可以不写,这个参数是为了安全考虑,postMessage()方法只会将message传递给指定窗口,当然如果愿意也可以建参数设置为"*",这样可以传递给任意窗口,如果要指定和当前窗口同源的话设置为"/"。

http://localhost:6000/a.html 

 
 
 
 
  1.   
  2.   
  3.   
  4.   
  5.   
  6.   
  7.   
  8.   
  9.       
  10.     发送消息  
  
  •   
  •   
  •   
  •   
  • http://localhost:7000/b.html 

     
     
     
     
    1.   
    2.   
    3.   
    4.   
    5.   
    6.   
    7.   
    8.   
    9.     Hello World!  
      
  •   
  •   
  •   
  • 这样我们就可以接收任何窗口传递来的消息了,为了安全起见,我们利用这时候的MessageEvent对象判断了一下消息源,MessageEvent对象,这个对象中包含很多东西。

    1. data:顾名思义,是传递来的message
    2. source:发送消息的窗口对象
    3. origin:发送消息窗口的源(协议+主机+端口号)

    使用postMessage方法比以上方法用起来要轻便,不必有太多的繁琐操作,可以说postMessage是对于解决跨域来说是一个比较好的解决方案,不会显得代码特别的臃肿,并且各个浏览器又有良好的支持。

    跨域资源共享(CORS)

    CORS:全称"跨域资源共享"(Cross-origin resource sharing)。CORS需要浏览器和服务器同时支持,才可以实现跨域请求,目前几乎所有浏览器都支持CORS,IE则不能低于IE10。CORS的整个过程都由浏览器自动完成,前端无需做任何设置,跟平时发送ajax请求并无差异。CORS的关键在于服务器,只要服务器实现CORS接口,就可以实现跨域通信。

    跨域资源共享(CORS) 是一种机制,它使用额外的HTTP头来告诉浏览器让运行在一个origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域、协议或端口请求一个资源时,资源会发起一个跨域HTTP请求。在上面说过src是不受同源策略限制的,但是出于安全原因,浏览器限制从脚本内发起的跨源HTTP请求。例如,XMLHttpRequest和FetchAPI遵循同源策略。这意味着使用这些API的Web应用程序只能从加载应用程序的同一个域请求HTTP资源,除非响应报文包含了正确CORS响应头。

    所有CORS相关的的头都是Access-Control为前缀的。下面是每个头的一些细节。

    字段 描述
    Access-Control-Allow-Methods该字段必需,它的值是逗号分隔的一个字符串,表明服务器支持的所有跨域请求的方法。注意,返回的是所有支持的方法,而不单是浏览器请求的那个方法。这是为了避免多次"预检"请求
    Access-Control-Allow-Headers如果浏览器请求包括Access-Control-Request-Headers字段,则Access-Control-Allow-Headers字段是必需的。它也是一个逗号分隔的字符串,表明服务器支持的所有头信息字段,不限于浏览器在"预检"中请求的字段
    Access-Control-Allow-Credentials该字段与简单请求时的含义相同。
    Access-Control-Max-Age该字段可选,用来指定本次预检请求的有效期,单位为秒。上面结果中,有效期是20天(1728000秒),即允许缓存该条回应1728000秒(即20天),在此期间,不用发出另一条预检请求。

     

     
     
     
     
    1. import express from "express";  
    2. import cors from "cors";  
    3. const app = express()  
    4. const  
    5. var corsOptions = {  
    6.   origin: 'http://example.com',  
    7.   optionsSuccessStatus: 200  
    8. }  
    9. app.get('/products/:id', cors(corsOptions), (req, res, next) => {  
    10.   res.json({msg: 'This is CORS-enabled for only example.com.'})  
    11. })  
    12. app.listen(80, function () {  
    13.   console.log('启用corba,端口:80')  
    14. })  

    使用CORS简单请求,非常容易,对于前端来说无需做任何配置,与发送普通ajax请求无异。唯一需要注意的是,需要携带cookie信息时,需要将withCredentials设置为true即可。CORS的配置,完全在后端设置,配置起来也比较容易,目前对于大部分浏览器兼容性也比较好,现在应用最多的就是CORS解决跨域了。

    WebSocket协议跨域

    WebSocket是HTML5新推出的一个API,通过WebSocket可以实现客户端与服务端的即时通信,如聊天室,服务数据同步渲染等等。WebSocket是点对点通信,服务端与客户端可以通过某种标识完成的。WebSocket是不受同源策略限制的所以可以利用这个特性直接与服务端进行点对点通信。

    以下示例没有使用HTML5的WebSocket而是使用的socket.io完成类似的功能操作。

    若若的说一句:其实我一直以为WebSocket与Ajax一样是受同源策略限制的,经过学习才发现不是的。真是学到老活到老(关谷口音)。O(∩_∩)O

    服务端: 

     
     
     
     
    1. var io = require('socket.io')(1234);  
    2. io.sockets.on('connection', (client) => {  
    3.     client.on('message', function (msg) { //监听到信息处理  
    4.         client.send('服务器收到了信息:' + msg);  
    5.     });  
    6.     client.on("disconnect", function () { //断开处理  
    7.         console.log("client has disconnected");  
    8.     });  
    9. });  
    10. console.log("listen 1234...");  

    客户端: 

     
     
     
     
    1. $(function () {  
    2.     var ioiosocket = io.connect('http://localhost:1234/');  
    3.     var $ul = $("ul");  
    4.     var $input = $("input");  
    5.     iosocket.on('connect', function () {  //接通处理  
    6.         $ul.append($('
    7. 连上啦
    8. '));  
    9.         iosocket.on('message', function (message) {  //收到信息处理  
    10.             $ul.append($('
    11. ').text(message));  
    12.         });  
    13.         iosocket.on('disconnect', function () { //断开处理  
    14.             $ul.append('
    15. Disconnected
    16. ');  
    17.         });  
    18.     });  
    19.     $input.keypress(function (event) {  
    20.         if (event.which == 13) { //回车  
    21.             event.preventDefault();  
    22.             console.log("send : " + $input.val());  
    23.             iosocket.send($input.val());  
    24.             $input.val('');  
    25.         }  
    26.     });  
    27. });  

    Websocket既然能支持跨域方法,那就是说,一个开放给公网的Websocket服务任何人都能访问,这样的话会使数据变得很不安全,所以可以通过对连接域名进行认证即可。

    服务器反代

    学习路程首先了解了一下什么是反代,在计算机网络中,反向代理是代理服务器的一种。服务器根据客户端的请求,从其关联的一组或多组后端服务器(如Web服务器)上获取资源,然后再将这些资源返回给客户端,客户端只会得知反向代理的IP地址,而不知道在代理服务器后面的服务器簇的存在。 -- 节选自百度百科

    反向代理服务器:就nginx把http请求转发到另一个或者一些服务器上。从而轻松实现跨域访问。比如服务器中分别部署了N个服务器,当客户端发起请求时不用直接请求服务器中N个节点上的服务,只需要访问我们的代理服务器就行了,代理服务器根据请求内容分发到不同服务器节点。这仅是一种使用场景,当然还可以做负载均衡等。

    反向代理理解起来不是特别的难,平时生活中最常见的例子,当我们拨打人工客服的时候,并不是直接拨打客服的某一个电话号码,而是拨打总机号码,当我们拨打然后由总机进行处理,然后再分发给不同的客服人员。r然而当服务人员需要让你挂断电话等待回拨的时候,也不是直接拨打到你的电话,同样是也通过总机之后再转发到你的电话。其实这个总机也就相当于反代服务器。虽然这个例子不太贴切但是多多少少就是这个意思。

    由于不太懂Nginx不知道该如何处理这个部分,只是对反向代理做了一个简单的了解,等以后学习了Nginx会补上相关代码。

    Nodejs代理跨域

    使用Nodejs进行跨域在我看来,就是使用Node服务做了一个中间代理转发,其原理和反向代理差不多,当访问某一个URL时需要通过服务器分发到另一个服务器URL地址中。这里就不过多的赘述了,直接看代码吧。

    示例代码入下:

    main.js 

     
     
     
     
    1. import http from "http";  
    2. import httpProxy from "http-proxy";  
    3. // 新建一个代理 Proxy Server 对象    
    4. const proxy = httpProxy.createProxyServer({});     
    5. // 捕获异常    
    6. proxy.on('error', function (err, req, res) {    
    7.   res.writeHead(500, {    
    8.     'Content-Type': 'text/plain'    
    9.   });    
    10.   res.end('error');    
    11. });       
    12. // 在每次请求中,调用 proxy.web(req, res config) 方法进行请求分发    
    13. const server = http.createServer((req, res) => {   
    14.   // 在这里可以自定义你的路由分发    
    15.   let host = req.headers.host, ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress;    
    16.   switch(host){    
    17.     case 'www.a.com':     
    18.         proxy.web(req, res, { target: 'http://localhost:3000' });    
    19.         break;    
    20.     case 'www.b.com':    
    21.         proxy.web(req, res, { target: 'http://localhost:4000' });    
    22.         break;  
    23.     default:    
    24.         res.writeHead(200, {    
    25.             'Content-Type': 'text/plain'    
    26.         });    
    27.         res.end('Hello Aaron!');    
    28.   }    
    29. });    
    30. server.listen(8080);  

    如代码所示,当访问www.a.com的时候,请求就被转发到了3000接口上,访问www.b.com时就被转发到了4000这个接口上。这样就简单的完成了一个反向代理的工作。

    在使用vue开发的时候难免也会遇到跨域问题,或许你根本就没有遇到,可能你们正好处于同一个域里面,还有一种可能就是,后端同学或者运维同学已经处理好有关跨域的相关操作。但是当在开发过程中遇到跨域的时候,什么前端应该有对应的解决办法。vue-cli是基于Node服务

    当前文章:前端开发|那些年曾谈起的跨域
    标题网址:http://www.gawzjz.com/qtweb/news13/170763.html

    网站建设、网络推广公司-创新互联,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等

    广告

    声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 创新互联

    猜你还喜欢下面的内容

    用户体验知识

    分类信息网站