跨域及解决方案
同源策略
同源策略是:浏览器的安全策略,拥有相同的协议、域名、端口号的网址间才可以相互访问资源
跨域只存在于浏览器端,浏览器为web提供了访问入口,就是在地址栏输入要访问的地址或点击某个连接就可以了,这是一种开放的形态,为了保证用户的信息安全,防止恶意网站窃取数据,所以要对它有所限制
1》不同源的页面不能获取cookie、localStorage、sessionStorage;
2》不同源的话,Ajax请求结果会被浏览器拦截
3》获取不到非同源的dom
DOM同源:不同源页面不能获取DOM,防止恶意网站利用iframe嵌入网站并迷惑用户,窃取信息(如果别人嵌入一个和银行长得一样的网站,只是url不同,如果你输入了账号等信息,就被别人窃取了信息)
跨域是可以发送请求的,只是可能被浏览器拦截了
相关疑问:
1、跨域请求是在发送前直接被拦截还是服务器相应了然后被拦截呢?
- 有对于简单请求:浏览器会直接发送请求,服务端做出响应,浏览器根据响应的CORS头部判断是否拦截,如果不允许跨域就会被浏览器拦截并提示CORS错误,不会将其传递给前端JavaScript代码
- 对于复杂请求:浏览器会先发送一个预请求,然后检查响应头中的CORS信息, 如果符合规则,浏览器会发送实际的跨域请求;如果不符合规则,浏览器不会发送实际请求
2、什么是简单请求?
简单请求必须满足以下条件:
只能使用
GET
、POST
或HEAD
方法只能使用
Accept
、Accept-Language
、Content-Language
和Content-Type
。且Content-Type只能是application/x-www-form-urlencoded
、multipart/form-data
或text/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:指定是否允许请求携带凭证,值为
true
或false
。 - 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
适用于需要在多个页面间共享数据和状态的场景