什么是跨域?

2021-01-01  本文已影响0人  我是Msorry

什么是同源策略

只有协议、域名、端口三者完全相同的页面才能请求数据,否者会被浏览器阻止

浏览器规定:

如果 JS 运行在 源A里,那么就只能获取源A的数据,不能获取源B的数据,既不允许跨域,这样能保护用户隐私

同源策略限制:

  1. Cookie、LocalStorage 和 IndexDB 无法读取
  2. AJAX 请求不能发送

什么是跨域

一个域的页面去请求另一个域的数据,域名和端口都相同,但是请求路径不同,不属于跨域

CORS 跨域资源共享

对于前端开发者来说,CORS 通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现 AJAX 请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
实现CORS通信的关键是服务器(后端)。只要服务器实现了CORS接口,就可以跨源通信。

前端 AJAX 代码
var request = new XMLHttpRequest(); // 新建XMLHttpRequest对象

request.onreadystatechange = function () { // 状态发生变化时,函数被回调
  if (request.readyState === 4) { // 成功完成
    // 判断响应结果:
    if (request.status === 200) {
      // 成功,通过responseText拿到响应的文本:
      return success(request.responseText);
    } else {
      // 失败,根据响应码判断失败原因:
      return fail(request.status);
    }
  } else {
    // HTTP请求还在继续...
  }
}

// 发送请求:
request.open('GET', '/api/categories');
request.send();

后端服务器代码

添加响应头Access-Control-Allow-Origin给信任的网址

var http = require('http')
var server = http.creareSever()
server.on('request',(request,response)=>{
    response.setHeader('Access-Control-Allow-Origin', url)
})

JSONP 跨域

实现原理:动态加载js,任何网站都能请求js文件,script标签的src属性不受跨域影响,可以访问外部脚本文件

IE 不支持 CORS,但是必须要有一种方式跨域。当前网站创造一个script去请求另一个网站的js,js中夹带数据;这个js文件会在当前网站执行一个回调全局函数,回调里面就有我们的数据。回调的名字是可以随机生成的一个随机数,把这个名字以callback的形式传给后台,后台会把这个函数返回过来并执行。
优点:JSONP的优点是兼容IE,可以跨域,即使请求另外的域名,也能跨域
缺点:因为是script标签,它只能知道成功和失败,拿不到状态码,header;因为是script标签,只能发get请求,不支持post

实现过程

请求方——浏览器
响应方——服务器

  1. 请求方创建scriptsrc指向响应方,同时传递查询参数?callback=random
  2. 响应方根据查询参数callback构造random.call(null,'data')这样的响应
  3. 浏览器接收响应,就会执行random.call(null,'data')
  4. 请求方得到数据

标准写法

虽然写的是ajax,但是一点关系没有,是动态的script

$.ajax({
    url: "https://ryan.com",
 
    // The name of the callback parameter, as specified by the YQL service
    jsonp: "callback",
 
    // Tell jQuery we're expecting JSONP
    dataType: "jsonp",
 
    // Tell YQL what we want and that we want JSON
    data: {
        q: "select title,abstract,url from search.news where query=\"cat\"",
        format: "json"
    },
 
    // Work with the response
    success: function( response ) {
        console.log( response ); // server response
    }
});

手写JSONP

function jsonp(url){
  return new Promise((resolve,reject)=>{
    const random = 'ryanJSONPCallbackName' + Math.random()
    //拿到数据后调用resolve
    window[random] = (data)=>{resolve(data)}
    const script = document.createElement('script')
    script.src=`${url}?callback=${random}`
    script.onload=()=>{
      script.remove()
    }
    //出错调用reject
    script.onerror =()=>{
      reject()
    }
    document.body.appendChild(script)
  })
}

Websocket

WebSocket protocol是HTML5一种新的协议。它实现了浏览器与服务器全双工通信,同时允许跨域通讯,能更好的节省服务器资源和带宽并达到实时通讯的目的,是server push技术的一种很好的实现。
socket本质上是对 TCP/IP 的运用进行了一层封装,应用程序直接调用 socket API 即可进行通信。
socket是如何工作的呢?
分为 2 个部分,服务端需要建立 socket 来监听指定的地址,然后等待客户端来连接。而客户端则需要建立 socket 并与服务端的 socket 地址进行连接。

WebSocket 和 HTTP最大不同是

websocket的基本用法

// WebSocket 协议的 URL 使用 ws://开头,另外安全的 WebSocket 协议使用 wss://开头
// 只需要实例化 WebSocket,创建连接,然后服务端和客户端就可以相互发送和响应消息
var socket = new websocket('ws://echo.websocket.org');
//当 Browser 和 WebSocketServer 连接成功后,会触发 onopen
ws.onopen = function(){ws.send(“Test!”); }; 
// 如果连接失败,发送、接收数据失败或者处理数据出现错误,会触发 onerror 
ws.onerror = function(evt){console.log(“WebSocketError!”);};
// 当 Browser 接收到 WebSocketServer 发送过来的数据时,就会触发 onmessage 消息,参数 evt 中包含 Server 传输过来的数据
ws.onmessage = function(evt){console.log(evt.data);ws.close();}; 
// 如果你想往后端推送数据,可以使用
// 因为Web Socket只能接受和发送纯为本数据,所以对数稍微复杂的数据,可以把他转化为JSON字符串
ws.send(data);
// 当 Browser 接收到 WebSocketServer 端发送的关闭连接请求时,就会触发 onclose 消息。
ws.onclose = function(evt){console.log(“WebSocketClosed!”);}; 


postMessage

postMessage是HTML5 XMLHttpRequest Level 2中的API,且是为数不多可以跨域操作的window属性之一,它可用于解决以下三个场景的跨域数据传递:

postMessage的API

otherWindow.postMessage(message, targetOrigin, [transfer]);

其他窗口可以监听message事件

window.addEventListener("message", receiveMessage, false);

function receiveMessage(event)
{
  // For Chrome, the origin property is in the event.originalEvent
  // object. 
  // 这里不准确,chrome没有这个属性
  // var origin = event.origin || event.originalEvent.origin; 
  var origin = event.origin
  if (origin !== "http://example.org:8080")
    return;

  // ...
}

postMessage的具体使用示例

例如:A页面要给B页面发送消息

// A页面的域名是http://www.examplea.com:8080,在A页面的script标签下加入以下代码
const winpopup = window.open('http://www.exampleb.com:8888/b.html');
// 将第二个参数targetOrigin设置为要打开的B的域名,如果设置的和要打开的页面域名不一致,会导致消息发送不出去。
winpopup.postMessage('Hello B, I am A', 'http://www.examplea.com:8080');
function receiveMessage(event) {
    const origin = event.origin;
    // 判断信息来源是否为B
    if(origin !== "http://www.examplea.com:8080") {
        return;
    }
    // data: 从其他 window 中传递过来的对象。
    // do something
    consoel.log(event.data);
}
window.addEventListener('message', receiveMessage, false);
// B页面的域名是http://www.exampleb.com:8888,在B页面的script标签中加入以下代码

//当A页面postMessage被调用后,这个function被addEventListener调用
function receiveMessage(event)
{
  // event.origin是调用 postMessage  时消息发送方窗口的 origin, 即A页面
  // 判断信息来源是否为A
  if (event.origin !== "http://www.examplea.com:8080") {
    return;
  }
  // event.source 就当前弹出页的来源页面
  // event.data 是 "Hello B, I am A"
  // 假设你已经验证了所收到信息的origin, 可以把event.source 作为回信的对象,并且把event.origin作为targetOrigin
  event.source.postMessage("hi A! the secret response is: rheeeeet!", event.origin);
}

window.addEventListener("message", receiveMessage, false);

postMessage的使用注意事项

参考:https://juejin.cn/post/6847902216049836039#heading-19

上一篇 下一篇

猜你喜欢

热点阅读