跨域解决方案

发布于:2025-02-11 ⋅ 阅读:(59) ⋅ 点赞:(0)

跨域及解决方案

同源策略

同源策略是:浏览器的安全策略,拥有相同的协议、域名、端口号的网址间才可以相互访问资源

跨域只存在于浏览器端,浏览器为web提供了访问入口,就是在地址栏输入要访问的地址或点击某个连接就可以了,这是一种开放的形态,为了保证用户的信息安全,防止恶意网站窃取数据,所以要对它有所限制

1》不同源的页面不能获取cookie、localStorage、sessionStorage;

2》不同源的话,Ajax请求结果会被浏览器拦截

3》获取不到非同源的dom

DOM同源:不同源页面不能获取DOM,防止恶意网站利用iframe嵌入网站并迷惑用户,窃取信息(如果别人嵌入一个和银行长得一样的网站,只是url不同,如果你输入了账号等信息,就被别人窃取了信息)

跨域是可以发送请求的,只是可能被浏览器拦截了

相关疑问:

1、跨域请求是在发送前直接被拦截还是服务器相应了然后被拦截呢?

  • 有对于简单请求:浏览器会直接发送请求,服务端做出响应,浏览器根据响应的CORS头部判断是否拦截,如果不允许跨域就会被浏览器拦截并提示CORS错误,不会将其传递给前端JavaScript代码
  • 对于复杂请求:浏览器会先发送一个预请求,然后检查响应头中的CORS信息, 如果符合规则,浏览器会发送实际的跨域请求;如果不符合规则,浏览器不会发送实际请求

2、什么是简单请求?

简单请求必须满足以下条件:

  • 只能使用GETPOSTHEAD方法

  • 只能使用AcceptAccept-LanguageContent-LanguageContent-Type。且Content-Type只能是application/x-www-form-urlencodedmultipart/form-datatext/plain

3、同源能阻止CSRF攻击吗?

同源策略在一定程度上限制了跨域请求,增加了攻击难度,但它并不能完全阻止CSRF攻击;跨域请求伪造CSRF是利用了用户的信息发送了请求,同源策略并没有阻止请求的发送

跨域的解决方案

跨域资源共享CORS

跨域资源共享(Cross-Origin Resource Sharing,简称 CORS)

主要思想就是使用自定义的HTTP头部让浏览器与服务器进行沟通,从而决定响应是成功还是失败,它允许了浏览器向跨源服务器发送请求,从而克服了同源的限制

CORS 的头部字段

  • Access-Control-Allow-Origin:指定哪些源可以访问资源。可以是具体的源,如http://example.com,或者*通配符(不推荐,因为它不安全)。
  • Access-Control-Allow-Methods:指定允许的HTTP方法。
  • Access-Control-Allow-Headers:指定允许的自定义头部。
  • Access-Control-Allow-Credentials:指定是否允许请求携带凭证,值为truefalse
  • Access-Control-Max-Age:指定预检请求的结果可以被缓存多长时间

eg:

node服务端:

const express = require("express");
const server = express();
// 方案1,使用cors包
// const cors = require("cors");
// server.use(cors());
// 方案二,设置允许跨域的域名,请求方式,header类型
server.all("*", function (req, res, next) {
  //设置允许跨域的域名,*代表允许任意域名跨域
  res.header("Access-Control-Allow-Origin", "*"); //*代表允许任意域名跨域,我们也可以色湖之具体的
  // res.header("Access-Control-Allow-Origin", "http://localhost:8888");
  //允许的header类型
  res.header("Access-Control-Allow-Headers", "content-type");
  //跨域允许的请求方式
  res.header("Access-Control-Allow-Methods", "DELETE,PUT,POST,GET,OPTIONS");
  if (req.method.toLowerCase() == "options") {
    res.send(200); //让options尝试请求快速结束
  } else {
    next();
  }
});
server.post("/api/cors/resource", function (req, res) {
  console.log("我收到了请求"); // 即使在跨域的情况下,也会打印;还是可以接收到请求,并响应的
  setTimeout(() => {
    res.send("跨域问题解决了");
  }, 1000);
});
server.listen(8888, function () {
  console.log("服务启动成功8888");
});

客户端测试一下:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>跨域</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/axios/1.1.3/axios.min.js"></script>
  </head>
  <body>
    <script>
      axios({
        headers: {
          "Content-Type": "text/plain",
        },
        url: "http://localhost:8888/api/cors/resource",
        method: "post",
      }).then((res) => {
        document.write(res.data);
      });
    </script>
  </body>
</html>

优点:

  • 安全性:CORS提供了一种安全的方式来允许跨域请求,而不需要牺牲用户的安全
  • 灵活性:服务器可以精确控制哪些源可以访问资源,以及可以携带哪些头部和方法
  • 兼容性:现代浏览器都支持CORS

postMessage

HTML5引入的API,允许来自不同源的页面间安全地传递消息;常用于iframe嵌入,页面之间的通信

JSONP

原理:利用script标签的src属性,可以不受限制地从其他域加载资源

缺点:只能进行 get 请求;存在安全风险,如CSRF攻击和XSS漏洞;不支持复杂的数据类型,返回的数据必须是有效的JavaScript代码

由于从不同域加载资源,如果这个域不可信,响应中可能存在恶意内容,此时只能完全删除jsonp了;另外我们不好确定jsonp请求是否失败

具体实现

1、客户端定义回调函数:在客户端定义一个回调函数,这个函数将被服务器调用并传递数据

function handleRes(data) {
    console.log('res:', data);
}

2、动态创建

var script = document.createElement('script');
script.src = 'https://test.com/data?callback=handleRes';
document.head.appendChild(script);

3、服务器端返回数据:服务器端需要返回的数据被包裹在一个回调函数中,前端通过定义这个回调函数来处理返回的数据

var data = { name: 'jsonp', info: '浏览器返回的内容' };
var jsonpResponse = 'handleRes(' + JSON.stringify(data) + ');';
res.send(jsonpResponse);

nginx代理

通过nginx将请求转发到目标服务器,然后将响应返回给客户端。这样可以绕开跨域

websockets

什么是websockets

WebSockets 是一种网络通信协议,提供了在单个TCP连接上进行双向通信的能力。服务端可以主动向客户端推送数据。而且客户端和服务器可以在任何时刻互相发送数据,而不需要等待对方的请求或响应;

应用场景:

多人在线编辑、股票实时价格变动、在线聊天

工作原理:

WebSocket通过建立一个持久的连接,服务器可以主动推送数据到客户端。在传统的HTTP通信中,浏览器向服务器发送请求,服务器返回响应。这种模式在许多应用中都很常见,但它并不适合需要实时更新的场景。WebSocket解决了这个问题,通过建立一个持久的连接,服务器可以主动向客户端推送数据‌

WebSockets 的使用步骤

1.建立连接

客户端通过发送一个特殊的HTTP请求来请求升级到WebSocket连接

var socket = new WebSocket('wss://example.com/socket');

2.连接成功

服务器接受请求并响应,一旦握手成功,HTTP连接升级为WebSocket连接

socket.onopen = function(event) {
    console.log('Connection established');
};

3.发送和接收消息

连接成功后,客户端和服务器可以通过send方法发送消息,并通过事件监听器接收消息

socket.onmessage = function(event) {
    console.log('Message from server ', event.data);
};

// Send a message to the server
socket.send('Hello Server!');

4.错误处理和关闭连接

socket.onerror = function(error) {
    console.log('WebSocket Error ', error);
};

socket.onclose = function(event) {
    console.log('WebSocket is closed now.');
};

// Close the connection
socket.close();

安全性

WebSockets 可以通过WSS(WebSocket Secure)协议实现加密,这是一种在WebSocket上实现TLS/SSL的协议,类似于HTTPS

局限

并非所有的服务器都支持WebSockets;

一些防火墙和代理服务器可能会阻止WebSockets连接;

与HTTP不同,WebSockets没有广泛部署的缓存机制

websokets与http对比

1》通信模式:

HTTP:是一种无状态、请求/响应模式的协议。客户端发起请求,服务器响应请求,然后连接关闭。每次请求和响应都需要建立新的连接(在HTTP/1.1中,可以使用持久连接,但仍然是无状态的)。

WebSockets:提供全双工通信,允许服务器和客户端之间进行双向交互,无需每次通信都重新建立连接。一旦连接建立,它将保持开放,直到客户端或服务器决定关闭连接。

2》连接持久性:

HTTP:每次交互都需要建立新的连接,即使在HTTP/1.1中支持持久连接,连接仍然需要在每次请求后关闭或重新建立。

WebSockets:一旦建立连接,它将保持开放,直到明确关闭。适合需要频繁、实时通信的应用。

3》头部开销:

HTTP:每个请求和响应都需要携带完整的HTTP头部,这在频繁的通信中会增加数据传输的开销

WebSockets连接建立后,消息传输不需要携带HTTP头部,只有数据本身被发送,减少了开销。

4》安全性:

HTTP:可以通过HTTPS来实现加密通信。

WebSockets:可以通过WSS(WebSocket Secure)协议实现加密,类似于HTTPS。

5》适用场景:

HTTP:适用于请求/响应模式的通信,如网页加载、API调用等。

WebSockets:适用于需要实时、双向通信的场景,如在线游戏、聊天应用、实时数据更新等。

6》浏览器兼容性:

HTTP:所有现代浏览器都支持HTTP。

WebSockets:现代浏览器都支持WebSockets,但老旧浏览器可能不支持。

7》控制和限制:

HTTP:受到同源策略的限制,跨域请求需要服务器端支持CORS。

WebSockets:没有同源策略的限制,可以跨域通信,但出于安全考虑,建议使用WSS。

8》服务器负载:

HTTP:频繁的请求可能导致服务器负载较高,尤其是在高流量的应用中。

WebSockets:由于连接持久,可减少服务器的连接建立和关闭的负载,但服务器要能够处理长时间保持的连接。

document.domain

当两个具有相同主域但不同子域的页面需要通信时,可以通过设置document.domain属性为共同的主域来实现。

SharedWorker

适用于需要在多个页面间共享数据和状态的场景