说一下 https?
https 是基于 SSL 加密的 http 协议
SSL(Secure Sockets Layer) 安全套接层,是一种安全协议,经历了 SSL 1.0、2.0、3.0 版本后发展成了标准安全协议 - TLS (Transport Layer Security) 传输层安全性协议。
CA(数字证书)
现实中,通过CA(Certificate Authority)来保证public key的真实性。CA也是基于非对称加密算法来工作。有了CA,B会先把自己的public key(和一些其他信息)交给CA。CA用自己的private key加密这些数据,加密完的数据称为B的数字证书。现在B要向A传递public key,B传递的是CA加密之后的数字证书。A收到以后,会通过CA发布的CA证书(包含了CA的public key),来解密B的数字证书,从而获得B的public key。
但是等等,A怎么确保CA证书不被劫持。C完全可以把一个假的CA证书发给A,进而欺骗A。CA的大杀器就是,CA把自己的CA证书集成在了浏览器和操作系统里面。A拿到浏览器或者操作系统的时候,已经有了CA证书,没有必要通过网络获取,那自然也不存在劫持的问题。
实际使用
非对称加密算法比对称加密算法要复杂的多,处理起来也要慢得多。如果所有的网络数据都用非对称加密算法来加密,那效率会很低。所以在实际中,非对称加密只会用来传递一条信息,那就是用于对称加密的密钥。当用于对称加密的密钥确定了,A和B还是通过对称加密算法进行网络通信。这样,既保证了网络通信的安全性,又不影响效率,A和B也不用见面商量密钥了。
所以,在现代,A和B之间要进行安全,省心的网络通信,需要经过以下几个步骤
- 通过CA体系交换public key
- 通过非对称加密算法,交换用于对称加密的密钥
- 通过对称加密算法,加密正常的网络通信
这基本就是SSL/TLS的工作过程了。
说一下 http2.0?
简要概括:http2.0 是基于 1999 年发布的 http1.0 之后的首次更新。
提升访问速度
:(可以对于,请求资源所需时间更少,访问速度更快,相比 http1.0)
允许多路复用
: 多路复用允许同时通过单一的 HTTP/2 连接发起多重的请求-响应消息。
多路复用因连接共享,每个request都有连接共享机制,一个request对应一个id,允许同时通过单一的 HTTP/2 连接发送多重请求-响应信息,接收方再根据request的id将request再归属到各自不同的服务端请求里面。改善了:在 http1.1 中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量限 制(连接数量),超过限制会被阻塞。
二进制分帧
: HTTP2.0 会将所有的传输信息分割为更小的信息或者帧,并对他们进行二 进制编码
header压缩
:因为http中头部信息带有大量信息,而且每次都是重复发送的,所以使用encoder来减少需要传送的header的大小,并且通讯双方都会保存一份header fields,这样就避免了header的传输,也减少了需要传输的大小。
服务器端推送
:
当浏览器请求一个网页时,服务器将会发回HTML,在服务器开始发送JavaScript、图片和CSS前,服务器需要等待浏览器解析HTML和发送所有内嵌资源的请求。服务器推送服务通过“推送”那些它认为客户端将会需要的内容到客户端的缓存中,以此来避免往返的延迟。
Http2.0的推送能否取代websocket?
因为HTTP2的推送是浏览器和服务端之间的一个推送的概念,比如我在HTTP2中请求 a.html 服务端会自动把css和一些其他资源一并返回,
而websocket的推送是有API的,可以在客户端手动控制的,这就是区别。
说一下 http3.0?
- 减少了TCP三次握手及TLS握手的时间
不同于HTTP1.0/1.1和HTTPS,HTTP2.0的TCP传输,HTTPS和HTTP2还需要TLS协议进行安全传输,会有两个握手的延迟,3.0采用UDP协议的QUIC,只需要一次交互
- 多路复用丢包时的线头阻塞问题
QUIC保留了HTTP2.0多路复用的特性,在之前的多路复用过程中,同一个TCP连接上有多个stream,假如其中一个stream丢包,在重传前后的stream都会受到影响,而QUIC中一个连接上的多个stream之间没有依赖。所以发生丢包时,只会影响当前的stream,也就避免了线头阻塞问题
- 优化重传策略
- 流量控制
- 连接迁移
常见的状态码?
1**:请求正在处理
2**:请求成功
- 200:请求正在处理
- 204:请求处理成功,但没有任何资源返回给客户端,常用于单向通信
- 206: 请求成功,响应报文中包含由 Content-Range 指定范围的实体内容
3**:重定向,需要附加操作已完成请求
-
301:永久重定向,表示请求的资源已经永久的搬到了其他位置,将旧的网址替换为了重定向之后的网址
-
302:临时重定向,表示请求的资源临时搬到了其他位置,搜索搜索引擎认为新的网址是暂时的
-
303表示请求资源存在另一个URI,应使用GET定向获取请求资源
-
303功能与302一样,区别只是303明确客户端应该使用GET访问
-
304:资源已找到,但不满足条件
eg:从网站A(网站比较烂)上做了一个302跳转到网站B(搜索排名很靠前),这时候有时搜索引擎会使用网站B的内容,但却收录了网站A的地址,这样在不知不觉间,网站B在为网站A作贡献,网站A的排名就靠前了。301跳转对查找引擎是一种对照驯良的跳转编制,也是查找引擎能够遭遇的跳转编制,它告诉查找引擎,这个地址弃用了,永远转向一个新地址,可以转移新域名的权重。而302重定向很容易被搜索引擎误认为是利用多个域名指向同一网站,那么你的网站就会被封掉,罪名是“利用重复的内容来干扰Google搜索结果的网站排名”。
4**:客户端错误
- 400:请求存在语法或者参数错误,服务器不理解(eg:前端提交的字段名称或者字段类型和后台的实体类不一样,或者参数个数不一致,导致无法封装。再比如前端提交到后台的数据应该是JSON字符串类型,而前端没有将对象转化为字符串类型,也会返回HTTP请求状态码400。)
也不一定是前端的问题,可能后端接收的参数配置不对也会报400
- 403:对请求资源的访问被禁止(eg:403错误 会在 fetch axios等 第一次发送的时候,如果method 不正确 delete 写成del,不管 url是否正确 会返回403 不让再次访问)
- 404:服务器找不多请求的资源
5**:服务器错误
- 500:服务器执行请求的时候出错
- 503:服务器无法处理请求,服务器正在维护或者超负载运行。
get与post请求的区别?
a. GET 是将参数写在 URL 中 ?
的后面,并用 &
分隔不同参数;而 POST 是将信息存放在 Message Body
中传送,参数‘不会’显示在 URL 中(Restful规范中是这样,但post在有需要时可以把参数放URL里)。GET方式需要使用Request.QueryString来取得变量的值,而POST方式通过Request.Form来获取变量的值。也就是说Get是通过地址栏来传值,而Post是通过提交表单来传值。
b. GET请求提交的数据有长度限制(HTTP 协议本身没有限制 URL 及正文长度,对 URL 的限制大多是浏览器和服务器的原因),POST请求没有内容长度限制。
ps:GET传递数据的长度是2048个字节,这个数字不小,远超过256这个曾经认为无比正确的数字. 另外POST方式传递的数据不受这个长度影响,它的值通过浏览器
header传输
而不是url.
c. GET请求返回的内容会被浏览器缓存起来。而每次提交POST请求,浏览器不会缓存POST请求返回的内容。
d. GET对数据进行查询,POST主要对数据进行增删改!简单说,GET是只读,POST是写。
e. 关于安全性,GET 请求方式从浏览器的 URL 地址就可以看到参数;所以post更安全,其实无论是 GET 还是 POST 其实都是不安全的,因为 HTTP 协议是明文传输,只要拦截封包便能轻易获取重要资讯。想要安全传输资料,必须使用 SSL/TLS来加密封包,也就是 HTTPS。
那为什么推崇使用post来处理敏感数据呢?
因为get的记录会保存在浏览器,上网日志中,而使用Post,因为数据不会记录存储在浏览器的记录和网址访问记录中,这样会有更大的安全性。
f.一个误区 说GET产生一个TCP数据包;POST产生两个TCP数据包
其说法:对于GET方式的请求,浏览器会把http header和data一并发送出去,服务端响应200,请求成功。
对于POST方式的请求,浏览器会先发送http header给服务端,告诉服务端等一下会有数据过来,服务端响应100 continue,告诉浏览器我已经准备接收数据,浏览器再post发送一个data给服务端,服务端响应200,请求成功。
为其正名:上面所说的post会比get多一个tcp包其实不太严谨。多发的那个expect 100 continue header报文,是由客户端对http的post和get的请求策略决定
的,目的是为了避免浪费资源,如带宽,数据传输消耗的时间等等。所以客户端会在发送header的时候添加expect 100去探探路,如果失败了就不用继续发送data,从而减少了资源的浪费。所以是否再发送一个包取决了客户端的实现策略,和get/post并没什么关系。有的客户端比如fireFox就只发送一个包。
GET产生一个TCP数据包;POST产生两个TCP数据包。
TCP三次握手和四次挥手
三次握手
第一次握手:客户端发送标志位[SYN=1]
和序号[seq=x]
的报文,请求建立连接。之后,客户端从“CLOSED”进入“SYN-SEND”,服务器被迫打开从“CLOSED”进入“LISTEN”。
第二次握手:服务器发送标志位[SYN=1,ACK=1]
和序号[seq=y,ack=x+1]
的报文,确认客户端的seq有效,并同意创建新连接。之后,服务器从“LISTEN”进入“SYN-RCVD”。
第三次握手:客户端发送标志位[ACK=1]
和序号[seq=x+1,ack=y+1]
的报文,确认收到服务器同意连接的序号。之后,客户端从“STN-SEND”进入“ESTABLISHED”,服务器从“SYN-RCVD”进入“ESTABLISHED”。
为什么建立连接时候,要进行第三次握手?
原因:第三次握手可以减少服务器的开销和资源浪费
假设现在是“两次握手”就成功的情况下建立链接:
1、第一次请求:客户端向服务器发起建立连接请求,由于网络原因迟迟未到服务器,客户端一般会设置一个超时时间,这种情况下就会关闭第一次连接请求的创建,重新发起第二次请求连接。
2、第二次请求:客户端向服务器发起建立连接请求,这次正常到达了服务器,服务器也做出了响应,第二次握手成功,就开启端口建立连接。
3、网络延迟的第一次请求:在过了一段时间后,延迟的第一次请求终于到达了服务器,服务器也做出了响应,第二次握手成功,又开启端口建立连接,但是由于客户端已经关闭了对第一次请求连接的创建,这个连接就再也不会有数据传回服务器,但是服务器又不知道,该端口就会一直开着。
4、网络延迟导致的超时重传连接多了,就会照成服务器开启过多端口处理一些失效的连接,导致服务器资源浪费。
所以,客户端才需要第三次握手来告诉服务器,有没有收到第二次握手时服务器发送的数据,有则建立TCP连接,没有则建立TCP连接失败,服务器也会关闭相应的连接端口。
三次握手过程中可以携带数据吗?
其实第三次握手的时候,是可以携带数据的。但是,第一次、第二次握手不可以携带数据
为什么这样呢?大家可以想一个问题,假如第一次握手可以携带数据的话,如果有人要恶意攻击服务器,那他每次都在第一次握手中的 SYN 报文中放入大量的数据。因为攻击者根本就不理服务器的接收、发送能力是否正常,然后疯狂着重复发 SYN 报文的话,这会让服务器花费很多时间、内存空间来接收这些报文。
也就是说,第一次握手不可以放数据,其中一个简单的原因就是会让服务器更加容易受到攻击了。而对于第三次的话,此时客户端已经处于 ESTABLISHED 状态。对于客户端来说,他已经建立起连接了,并且也已经知道服务器的接收、发送能力是正常的了,所以能携带数据也没啥毛病。
四次挥手
第一次挥手:客户端发送标志位[FIN=1]
和序号[seq=x]
的报文,请求释放连接,客户端从“ESTABLISHED”进入“FIN-WAIT-1”。
第二次挥手:服务器发送标志位[ACK=1]
和序号[seq=y,ack=x+1]
的报文,确认收到释放连接请求,服务器从“ESTABLISHED”进入“CLOSE-WAIT”,客户端从“FIN-WAIT-1”进入“FIN-WAIT-2”。
第三次挥手:服务器发送标志位[FIN=1,ACK=1]
和序号[seq=z,ack=x+1]
的报文,同意释放连接,服务器从“CLOSE-WAIT”进入“LAST-ACK”。
第四次挥手:客户端发送标志位[ACK=1]
和序号[seq=x+1,ack=z+1]
的报文,确认收到同意释放连接请求,服务器从“LAST-ACK”进入“CLOSED”,客户端等待2MSL时间,再从“FIN-WAIT-2”进入“CLOSED”。
为什么释放连接时候,要进行四次挥手而不是三次?
原因:服务器释放连接前有必要数据需要处理
建立连接和释放连接服务器始终都是被动方,建立连接的时候服务器并不需要任何准备,而释放连接时候不同,服务器突然收到释放连接,会有一些必要数据进行处理,所以只能先发送一个确认报文,等数据处理完了,再发送释放报文。
为什么第四次挥手请求发出后,客户端要等待2MSL时间?
原因:客户端想确认服务器是否成功收到第四次挥手的确认报文
MSL即Maximum Segment Lifetime:指的是一段TCP报文在传输过程中的最大生命周期,也是服务器第三次挥手发出的释放报文和客户端第四次挥手发出的确认报文所能保持有效的最大时长。
服务器在第三次挥手后,会等待1MSL,如果没收到客户端的确认报文,就会超时重传。这样子,客户端就会在第四次挥手后的2MSL时间内收到重传的报文,之后客户端重新发出第四次挥手的报文,重新等待2MSL,直到2MSL时间内服务器没有重传,就证明服务器已经收到第四次挥手的报文,关闭连接端口了。
WebSocket相关
创建一个简单的websocket
socket.js
const ws = require("nodejs-websocket");
console.log("开始建立连接...")
const socket = ws.createServer(function(conn){
conn.on("text", function (str) {
console.log("message:"+str)
let msg = "你好,这里是是日前端~"
// let msg = 'websocket处于正常状态'
setInterval(() => {
conn.sendText(msg);
}, 3000)
})
conn.on("close", function (code, reason) {
console.log("关闭连接")
});
conn.on("error", function (code, reason) {
console.log("异常关闭", code, reason)
});
}).listen(8666)
console.log("WebSocket建立完毕")
module.exports = socket
客户端直接调用
test.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<div id="test"></div>
<script>
if(window.WebSocket){
var ws = new WebSocket('ws://localhost:8666');
ws.onopen = function(e){
console.log("连接服务器成功");
// 向服务器发送消息
ws.send("来自客户端的消息");
}
ws.onclose = function(e){
console.log("服务器关闭");
}
ws.onerror = function(){
console.log("连接出错");
}
// 接收服务器的消息
ws.onmessage = function(e){
let message = "message:"+e.data+"";
console.log(message);
document.getElementById('test').innerHTML = message
}
}
</script>
</body>
</html>
几种实时通信
-
1)客户端轮询:传统意义上的短轮询(Short Polling);
let xhr = new XMLHttpRequest(); setInterval(function(){ xhr.open('GET','/user'); xhr.onreadystatechange = function(){ }; xhr.send(); },1000)
这种方式的优点是比较简单,易于理解,实现起来也没有什么技术难点。缺点是显而易见的,这种方式由于需要不断的建立http连接,严重浪费了服务器端和客户端的资源。尤其是在客户端,距离来说,如果有数量级相对比较大的人同时位于基于短轮询的应用中,那么每一个用户的客户端都会疯狂的向服务器端发送http请求,而且不会间断。人数越多,服务器端压力越大,这是很不合理的。
因此短轮询不适用于那些同时在线用户数量比较大,并且很注重性能的Web应用
-
2)服务器端轮询:长轮询(Long Polling);
function ajax(){ var xhr = new XMLHttpRequest(); xhr.open('GET','/user'); xhr.onreadystatechange = function(){ ajax(); }; xhr.send(); }
当服务器收到客户端发来的请求后,服务器端不会直接进行响应,而是先将这个请求挂起,然后判断服务器端数据是否有更新。如果有更新,则进行响应,如果一直没有数据,则到达一定的时间限制(服务器端设置)才返回。 。 客户端JavaScript响应处理函数会在处理完服务器返回的信息后,再次发出请求,重新建立连接。
长轮询和短轮询比起来,明显减少了很多不必要的http请求次数,相比之下节约了资源。长轮询的缺点在于,连接挂起也会导致资源的浪费。
轮询与长轮询都是基于HTTP的,两者本身存在着缺陷:轮询需要更快的处理速度;长轮询则更要求处理并发的能力;两者都是“被动型服务器”的体现:服务器不会主动推送信息,而是在客户端发送ajax请求后进行返回的响应。而理想的模型是"在服务器端数据有了变化后,可以主动推送给客户端",这种"主动型"服务器是解决这类问题的很好的方案
-
3)单向服务器推送:Server-Sent Events(SSE);
SSE是HTML5新增的功能,全称为Server-Sent Events。它可以允许服务推送数据到客户端。SSE在本质上就与之前的长轮询、短轮询不同,虽然都是基于http协议的,但是轮询需要客户端先发送请求。而SSE最大的特点就是不需要客户端发送请求,可以实现只要服务器端数据有更新,就可以马上发送到客户端。
原理:
SSE本质是发送的不是一次性的数据包,而是一个数据流。可以使用 HTTP 301 和 307 重定向与正常的 HTTP 请求一样。服务端连续不断的发送,客户端不会关闭连接,如果连接断开,浏览器会尝试重新连接。如果连接被关闭,客户端可以被告知使用 HTTP 204 无内容响应代码停止重新连接。
sse只适用于高级浏览器,ie不支持。因为ie上的XMLHttpRequest对象不支持获取部分的响应内容,只有在响应完成之后才能获取其内容。
SSE的优势很明显,它不需要建立或保持大量的客户端发往服务器端的请求,节约了很多资源,提升应用性能。并且后面会介绍道,SSE的实现非常简单,并且不需要依赖其他插件。
-
4)全双工通信:WebSocket。
WebSocket是Html5定义的一个新协议,与传统的http协议不同,该协议可以实现服务器与客户端之间全双工通信。
简单来说,首先需要在客户端和服务器端建立起一个连接,这部分需要http。连接一旦建立,客户端和服务器端就处于平等的地位,可以相互发送数据,不存在请求和响应的区别。
WebSocket的优点是实现了双向通信,缺点是服务器端的逻辑非常复杂。现在针对不同的后台语言有不同的插件可以使用。
原理:
WebSocket协议是借用HTTP协议的101 switchprotocol(服务器根据客户端的指定,将协议转换成为 Upgrade首部所列的协议)来达到协议转换的,从HTTP协议切换成WebSocket通信协议。
具体连接方式:
通过在请求头中增加 upgrade:websocket 及通信密钥(Sec-WebSocket-Key),使双方握手成功,建立全双工通信。
WebSocket客户端连接报文
WebSocket服务端响应报文
四种Web即时通信技术比较
从兼容性角度考虑,短轮询>长轮询>长连接SSE>WebSocket;
从性能方面考虑,WebSocket>长连接SSE>长轮询>短轮询。
短轮询 | 长轮询 | Websocket | sse | |
---|---|---|---|---|
通讯方式 | http | http | 基于TCP长连接通讯 | http |
触发方式 | 轮询 | 轮询 | 事件 | 事件 |
优点 | 兼容性好容错性强,实现简单 | 全双工通讯协议,性能开销小、安全性高,有一定可扩展性 | 实现简便,开发成本低 | |
缺点 | 安全性差,占较多的内存资源与请求数 | 安全性差,占较多的内存资源与请求数 | 传输数据需要进行二次解析,增加开发成本及难度 | 只适用高级浏览器 |
适用范围 | b/s服务 | b/s服务 | 网络游戏、银行交互和支付 | 服务端到客户端单向推送 |
ps:websocket的改进
一旦WebSocket连接建立后,后续数据都以帧序列
的形式传输。在客户端断开WebSocket连接或Server端中断连接前,不需要客户端和服务端重新发起连接请求。在海量并发及客户端与服务器交互负载流量大的情况下,极大的节省了网络带宽资源的消耗,有明显的性能优势,且客户端发送和接受消息是在同一个持久连接上发起,实现了“真·长链接”,实时性优势明显。
WebSocket有以下特点:
是真正的全双工方式,建立连接后客户端与服务器端是完全平等的,可以互相主动请求。而HTTP长连接基于HTTP,是传统的客户端对服务器发起请求的模式。
HTTP长连接中,每次数据交换除了真正的数据部分外,服务器和客户端还要大量交换HTTP header
,信息交换效率很低。Websocket协议通过第一个request建立了TCP连接之后,之后交换的数据都不需要发送 HTTP header就能交换数据,这显然和原有的HTTP协议有区别所以它需要对服务器和客户端都进行升级才能实现(主流浏览器都已支持HTML5)
WebSocket 使用的是 TCP 还是 UDP 协议?
websocket运用的协议是自己定义的协议
websocket的协议是在TCP/IP协议簇的应用层,
和http
在同一层。
“自己定义的协议”是指websocket
使用的是不同于http
的另一种应用层协议,但websocket
和http
都是基于TCP传输层的。
就像TCP协议对应于传输层,而HTTP协议对应于应用层,从本质上来说,二者没有可比性。Http协议是建立在TCP协议基础之上的
ps:HTTP为啥不选择UDP?
主要原因是要保证可靠传输。
如何实现扫码登录功能?
WebSocket和SocketIO的区别?
websocket和socket.io之间的区别为:性质不同、兼容不同、用途不同。
一、性质不同
1、websocket:websocket是一种让客户端和服务器之间能进行双向实时通信的技术
。
2、socket.io:socket.io是将WebSocket、AJAX和其它的通信方式全部封装成了统一的通信接口
。
二、兼容不同
1、websocket:在使用websocket时,,虽然主流浏览器都已经支持,但仍然可能有不兼容的情况
。
2、socket.io:在使用socket.io时,不用担心兼容问题
,底层会自动选用最佳的通信方式。
三、用途不同
1、websocket:websocket适合用于client和基于node搭建的服务端使用。
2、socket.io:socket.io适合进行服务端
和客户端
双向数据通信。
同源策略
跨域详解: juejin.cn/post/696910…
浏览器让出了同源策略的哪些安全性?
浏览器为了便利性让出了部分安全性
三种常见攻击
XSS
指的是跨脚本攻击,指的是攻击者在网页中嵌套,恶意脚本程序,当用户打开网页时,程序开始在浏览器上启动,盗取用户的cooks,从而盗取密码等信息,下载执行木马程序。
解决方法:XSS之所以会发生,是因为用户输入的数据变成了代码。因此,我们需要对用户输入的数据进行HTML转义处理,将其中的“尖括号”、“单引号”、“引号” 之类的特殊字符进行转义编码。
CSRF
CSRF攻击的全称是跨站请求伪造(cross site request forgery), 是一种对网站的恶意利用,你可以这么理解CSRF攻击:攻击者盗用了你的身份,以你的名义向第三方网站发送恶意请求。CRSF能做的事情包括利用你的身份发邮件、发短信、进行交易转账等等,甚至盗取你的账号。
解决方法:
cookie设置为HttpOnly
CSRF攻击很大程度上是利用了浏览器的cookie,为了防止站内的XSS漏洞盗取cookie,需要在cookie中设置"HttpOnly"属性,这样通过程序(如JavascriptS脚本、Applet等)就无法读取到cookie信息,避免了攻击者伪造cookie的情况出现。
通过Referer识别
根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址。在通常情况下,访问一个安全受限页面的请求都来自于同一个网站。
与XSS攻击的区别:尽管听起来跟XSS跨站脚本攻击有点相似,但事实上CSRF与XSS差别很大,XSS利用的是站点内的信任用户,而CSRF则是通过伪装来自受信任用户的请求来利用受信任的网站。
SQL注入
SQL注入:所谓SQL注入,就是通过把SQL命令伪装成正常的HTTP请求参数,传递到服务端,欺骗服务器最终执行恶意的SQL命令,达到入侵目的。攻击者可以利用SQL注入漏洞,查询非授权信息, 修改数据库服务器的数据,改变表结构,甚至是获取服务器root权限。总而言之,SQL注入漏洞的危害极大,攻击者采用的SQL指令,决定攻击的威力。当前涉及到大批量数据泄露的攻击事件,大部分都是通过利用SQL注入来实施
解决方法:使用预编译语句
文件上传漏洞
文件上传的时候,没有对于文件的类型进行处理,从而导致攻击者上传了一些恶意的脚本程序,从而达到攻击的目的。
解决方法:判断文件类型,使用随机数改写文件名和文件路径
跨域解决方案
JSONP
JSONP的原理是利用<script>
标签没有跨域限制
JSONP只支持GET
请求,CORS支持所有类型的HTTP请求。JSONP的优势在于支持老式浏览器,以及可以向不支持CORS的网站请求数据。
nginx反向代理
实现原理:同源策略是浏览器需要遵循的标准,而服务器向服务器发送请求则无需遵循同源策略,使用一个代理服务器,设置Access-Control-Allow-Origin等字段便可以解决浏览器和代理服务器之间的跨域问题,而服务器之间没有限制,便实现了浏览器和服务器之间跨域通信,并且可以实现代理多个请求到不同的服务器
CORS(cross-origin resource sharing)跨域资源共享
详见:www.ruanyifeng.com/blog/2016/0…
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
因此,实现CORS通信的关键是服务器。只要服务器实现了CORS接口,就可以跨源通信。
跨域资源共享CORS
是一种机制,它使用额外的 HTTP
头来告诉浏览器 让运行在一个 origin (domain) 上的 Web 应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器 「不同的域、协议或端口」请求一个资源时,资源会发起一个 「跨域 HTTP 请求」。
而在 cors 中会有
简单请求
和 复杂请求
的概念。
浏览器将CORS请求分成两类:简单请求(simple request)和非简单请求(not-so-simple request)。
只要同时满足以下两大条件,就属于简单请求。
(1) 请求方法是以下三种方法之一:
- HEAD
- GET
- POST
(2)HTTP的头信息不超出以下几种字段:
- Accept
- Accept-Language
- Content-Language
- Last-Event-ID
- Content-Type:只限于三个值
application/x-www-form-urlencoded
、multipart/form-data
、text/plain
PS:当GET请求加入了Authorization也算复杂请求
什么是复杂请求?
复杂请求就是预请求(可能对服务器数据产生副作用的HTTP请求方法,如put,delete都会对服务器数据进行更修改,所以要先询问服务器)。
跨域请求中,浏览器自发的发起的预请求,浏览器会查询到两次请求,第一次的请求参数是options,以检测试实际请求是否可以被浏览器接受
为什么需要?
w3c规范要求,对复杂请求,浏览器必须先使用options发起一个预检请求,从而获知服务器是否允许该跨域请求,服务器确认以后才能发起实际的HTTP请求,否则停止第二次正式请求。
那为什么我们不常见options请求呢?
因为大部分我们使用的是get,post请求,他们属于简单请求,而简单请求不会触发options请求。
那什么情况下会发生options请求呢?
- 请求方法不是get head post
- post 的content-type不是application/x-www-form-urlencode,multipart/form-data,text/plain [也就是把content-type设置成"application/json"]
- 请求设置了自定义的header字段: 比如业务需求,传一个字段,方面后端获取,不需要每个接口都传
例如设置了post请求的content-type:application/json,就会发生预请求
为什么需要设置成contentType:"application/json"?
ajax发送复杂的json数据结构, 处理方式困难, 服务器端难以解析, 所以就有了application/json这种类型(数据格式的声明)服务端好解析并且比较统一
常见的表单数据提交数据的编码类型(content-type)
协议规定 POST 提交的数据必须放在消息主体(entity-body)中,但协议并没有规定数据必须使用什么编码方式。
服务端通常是根据请求头(headers)中的 Content-Type 字段来获知请求中的消息主体是用何种方式编码,再对主体进行解析。
所以说到 POST 提交数据方案,包含了 Content-Type 编码方式和消息主体两部分。
(1)application/x-www-form-urlencoded
最常见的 POST 提交数据的方式。浏览器的原生 form 表单,如果不设置 enctype 属性,那么最终就会以 application/x-www-form-urlencoded 方式提交数据。
首先,Content-Type 被指定为 application/x-www-form-urlencoded;其次,提交的数据按照 key1=val1&key2=val2 的方式进行编码,key 和 val 都进行了 URL 转码。大部分服务端语言都对这种方式有很好的支持。
(2)application/json
这个 Content-Type 作为响应头大家肯定不陌生。实际上,现在越来越多的人把它作为请求头,用来告诉服务端消息主体是序列化后的 JSON 字符串。由于 JSON 规范的流行,除了低版本 IE 之外的各大浏览器都原生支持 JSON.stringify,服务端语言也都有处理 JSON 的函数,使用 JSON 不会遇上什么麻烦。
JSON 格式支持比键值对复杂得多的结构化数据。
Google 的 AngularJS 中的 Ajax 功能,默认就是提交 JSON 字符串。
(3)multipart/form-data
我们使用表单上传文件时,必须让 form 的 enctyped 等于这个值。
消息主体里按照字段个数又分为多个结构类似的部分,每部分都是以 –boundary 开始,紧接着内容描述信息,然后是回车,最后是字段具体内容(文本或二进制)。如果传输的是文件,还要包含文件名和文件类型信息。消息主体最后以 –boundary– 标示结束。
实现 CORS在前端配置还是后端
实现 CORS 通信的关键在于 服务器。
CORS需要浏览器和服务器同时支持。目前,所有浏览器都支持该功能,IE浏览器不能低于IE10。
整个CORS通信过程,都是浏览器自动完成,不需要用户参与。对于开发者来说,CORS通信与同源的AJAX通信没有差别,代码完全一样。浏览器一旦发现AJAX请求跨源,就会自动添加一些附加的头信息,有时还会多出一次附加的请求,但用户不会有感觉。
前端处理时,直接就是same-origin
,使用了一个http代理中间件
如果服务器认为这个请求可以接受,就在Access-Control-Allow-Origin
头部中回发相同的源信息
服务器收到预检请求之后,检查了Origin、Access-Control-Request-Method、Access-Control-Request-Headers之后,确认允许跨源请求就可以做出回应,responseHeader中需要返回请求头一样的请求体 request中有headers 返回体必须也要
因此,
实现CORS通信的关键是服务器
。只要服务器实现了CORS接口,就可以跨源通信。
websocket跨域
WebSocket 规范定义了一种 API,可在网络浏览器和服务器之间建立“套接字”连接。简单地说:客户端和服务器之间存在持久的连接,而且双方都可以随时开始发送数据。 这种方式本质没有使用了 HTTP 的响应头, 因此也没有跨域的限制,没有什么过多的解释直接上代码吧。
前端部分
<script>
let socket = new WebSocket("ws://localhost:8080");
socket.onopen = function() {
socket.send("图图的笔记");
};
socket.onmessage = function(e) {
console.log(e.data);
};
</script>
后端部分
const WebSocket = require("ws");
const server = new WebSocket.Server({ port: 8080 });
server.on("connection", function(socket) {
socket.on("message", function(data) {
socket.send(data);
});
});
正向代理
我们常说的代理也就是指正向代理,正向代理的过程,它隐藏了真实的请求客户端,服务端不知道真实的客户端是谁
,客户端请求的服务都被代理服务器代替来请求,某些科学上网工具扮演的就是典型的正向代理角色。用浏览器访问 www.google.com 时,被残忍的block,于是你可以在国外搭建一台代理服务器,让代理帮我去请求google.com,代理把请求返回的相应结构再返回给我。
nginx反向代理
反向代理隐藏了真实的服务端,当我们请求 www.baidu.com 的时候,就像拨打10086一样,背后可能有成千上万台服务器为我们服务,但具体是哪一台,你不知道,也不需要知道,你只需要知道反向代理服务器是谁就好了,www.baidu.com 就是我们的反向代理服务器,反向代理服务器会帮我们把请求转发到真实的服务器那里去。Nginx就是性能非常好的反向代理服务器,用来做负载均衡。
正向代理与反向代理的区别在于代理的对象不一样,正向代理代理的对象是客户端,反向代理代理的对象是服务端。
正向代理主要是用来解决访问限制问题;而反向代理则是提供负载均衡、安全防护等作用。
正向代理服务器,用户可以知道Web服务的数据库等系统的IP地址和端口号等敏感息,
由于暴露了这些关键信息,容易遭受到恶意攻击所以安全性比较差。