我不允许谁还不懂跨域问题!!😡

123 阅读5分钟

前言

跨域问题说实话,是个面试官就会问一嘴。如果回答的不完整或者不自信这太丢分了!为此我得好好写篇文章来梳理梳理。

同源策略

浏览器会拥有同源策略,它会阻止浏览器接收响应。请求能够发出去,但响应回不来。之所以会有同源策略,是从安全性角度来考虑的,如果没有同源策略,那么谁都可以写一个页面朝百度发送一个请求。那么后端数据毫无安全可言。 我们来分析一下http://192.168.3.1:3000/home http是协议 192.168.3.1是域名或ip地址 3000是端口号 /home是接口路径 同源策略是要求协议--域名--端口 都一致才称为同源。我们在开发过程中跨域问题是避免不了的。因此得想出一些解决方案来才行!!

解决方案

JSONP

<body>
<img src="https://img1.baidu.com/it/u=502992997"/>
</body>

图片地址放在标签上浏览器可以将图片资源加载出来。浏览器向该地址发送请求,将图片资源请求回来。这也是会跨域,但是不经过任何操作却能请求回来。这又是为啥呢?最开始同源策略极其严格,严格到任何资源只要不是同源的都请求不到,后来为了开发简单同源策略把img标签排除在外了。同理

也不会受同源策略的影响。还有媒体标签如: 等 JSONP的原理就是依据的这个来的。scr资源属性不受同源策略的影响
    
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.4.38/vue.cjs.js"></script>
    

这段源码会被加载出,但这种现在缺点很明显后端返回的响应体根本操作不了。那么我们又该怎么办呢?

    function jsonp(url,cb){
      return new Promise((resolve,reject)=>{
          const script =document.createElement('script');
          window[cb]=function(data){//function callback(){}
    
    }
          script.src=`${url}?cb=${cb}`;
    
       document.body.appendChild(script)
    })
    }
    jsonp('http://localhost:3000','callback').then(res=>{
    console.log(res)
    })
 const http=require('http');
 http.createServer(function(req,res){
    const query=new URL(req.url,`http://${req.headers.host}`).searchParms//截取路径参数的方法
    console.log(query.get('cb'));
    
    if(query.get('cb')){
      const cb=query.get('cb')//'callback'
      const data='hello world'
    const result=`${cb}("${data}")` //'callback(""hello world")'
       res.end(result)
    }
    
    res.end('hello world')
  }).listen(3000)
   

好啦现在一句话总结下JSOP的原理:借助script 标签的scr属性不受同源策略的影响,来发送请求。给后端携带一个参数 callback 并在前端定义callback函数体,后端返回callback的调用形式并将要响应的值作为callback的实参,当浏览器接收到响应后就会触发全局的callback函数从而让callback以参数的形式接收到后端响应。 缺点:需要后端配合,并且只能发送get请求。

cors

我们先看看下边的代码

    const xhr =new XMLHttpRequest();
    xhr.open('GET','http://localhost:3000')
    xhr.send();
    xhr.onreadystatechange=function(){
    if(xhr.readyState===4 && xhr.status===200){
       console.log(xhr.responseText);
      }
    }
    
  const http=require('http');
    
    http.createServer(function(req,res){
    
       res.writeHead(200,{
        'Access-Control-Allow-Origin':'*'
        'access-control-allow-methods':'GET'  //前端只允许发GET要不然还是会被跨域
    })
    
       res.end('hello world')
    
    }).listen(3000)
    

好啦以上代码可以一句话总结出:通过设置响应头中的某些字段:比如,access-control-allow-origin 设置允许的源来通知浏览器此时同源策略不需要生效。

proxy

    const xhr=new XMLHttpRequest();
    xhr.open('GET','http://localhost:3001')
    xhr.send();
    xhr.onreadystatechange=function(){
       if(xhr.readyState===4&&xhr.status===200){
           console.log(xhr.responseText);
       }
    }
    const http=require('http');
     http.createServer(function(req,res){
        res.writeHead(200,{
     "access-contol-allow-origin":"*"
    })
    http.request({
      host:'192.168.1.63'
      port:'3000',
      path:'/',
      method:'GET',
      headers:{}
    },proxyRes=>{
       proxyRes.on('data',function(data){
            res.end(data.toString())
       })
     }).end()
    }).listen(3000);

其原理可以有以下总结:

代理服务器的工作原理

  1. 客户端请求:客户端向代理服务器发送请求。

  2. 代理服务器转发请求:代理服务器接收请求后,将其转发到目标服务器。

  3. 目标服务器响应:目标服务器处理请求并返回响应。

  4. 代理服务器转发响应:代理服务器接收目标服务器的响应,并将其转发回客户端。

这种方法原理我们也可以用在vue项目中的vite.config.js文件里边配置。

    server:{
         proxy:{
         '/api':{
          target:'http://ustbhuangyi.com/sell/',
          changeOrigin:true,
          rewrite:(path)=>path.replace(/^\/api/,'/api')
    }
    }

其实在vite里边配置这个对象就相当于在启动前边所演示的后端项目。但是如果上线的话,把项目打包部署到服务器上去的话这段代码就不会存在了,vite只是一个构建工具。vite的源码是不在的,所以只是在开发过程中有用,部署后就没用了。

nginx

nginx代理的原理跟node代理是一样的,相当于一个node代理。服务器上有一个前端项目,和后端项目。我们可以在服务器上装一个nginx(工具软件)我们在配置文件里配置前端地址,后端地址当后端返回了一个响应由nginx接收到nginx就相当于启动了node服务一样再返回给前端。总结:.Nginx 解决跨域问题的过程是通过配置反向代理,将客户端请求转发到目标服务器,并在响应中添加必要的 CORS 头,从而使浏览器允许跨域请求。

WebSocket

前边介绍的几种都是很常见的,接下来我们来看看WebSocket吧!!Socket和http一样都是用来进行超文本传输的,但是http与Socket有一个非常大的区别,http是一个请求发过去,一个请求回来就算结束了,Socket则不然一旦建立了连接就不会断开了,除非人为!!

    <script>
        function myWebSocket(url,parms={}){
           return new Promise(function(resolve,reject){
             const socket=new webSocket(url)
              socket.onopen=()=>{
               socket.send(JSON.stringify(parms))
            }
           socket.onmessage=function(e){
        
            console.log(e.data);
        }
        })
        
        }
        
        
     myWebSocket('ws://localhost:3000',{age:18}).then(res=>{
           consloe.log(res);
        })  
    </script>
    const WebSocket=require('ws');
    const ws=new WebSocket.Server({port:3000});
    let count=0
    ws.on('connection',function(obj){
       obj.on('message',function(data){
         obj.send('哈哈') 
        setInterval(()=>{
         obj.send(++count)
    },2000)
      })
    })    

总结:socket 协议天生不受同源策略的影响。

postMessage

当页面一通过iframe嵌套了页面二,这两个页面上已经无法进行通讯的,因为跨域了,但是使用postMessage可以实现跨域

document.domain

两个页面通过iframe进行嵌套,且两个页面的二级域名是一致的!!