网络相关面试题整理

430 阅读1小时+

HTTP/HTTPS

HTTP 状态码知道哪些?分别什么意思?

  • 200:OK 请求成功。一般用于GET与POST请求。
  • 201:Created 已创建。成功请求并创建了新的资源。
  • 202:Accepted 已接受。已经接受请求,但未处理完成。
  • 301:Moved Permanently 永久重定向。请求的资源已被永久的移动到新URI,返回信息会包括新的URI,浏览器会自动定向到新URI。今后任何新的请求都应使用新的URI代替。
  • 302:Found 临时重定向。与301类似。但资源只是临时被移动。客户端应继续使用原有URI
  • 304:Not Modified 未修改。所请求的资源未修改,服务器返回此状态码时,不会返回任何资源。客户端通常会缓存访问过的资源,通过提供一个头信息指出客户端希望只返回在指定日期之后修改的资源。
  • 400:Bad Request 客户端请求的语法错误,服务器无法理解。
  • 401:Unauthorized 请求要求用户的身份认证。
  • 403:Forbidden 服务器理解请求客户端的请求,但是拒绝执行此请求。
  • 404:Not Found 服务器无法根据客户端的请求找到资源(网页)。通过此代码,网站设计人员可设置"您所请求的资源无法找到"的个性页面。
  • 414:Request-URI Too Large 请求的URI过长(URI通常为网址),服务器无法处理。
  • 500:Internal Server Error 服务器内部错误,无法完成请求。
  • 501:Not Implemented 服务器不支持请求的功能,无法完成请求。

GET 和 POST 的区别

  • 错误但可用版
    • GET 在浏览器回退时是无害的,而 POST 会再次提交请求。
    • GET 产生的 URL 地址可以被 Bookmark,而 POST 不可以。
    • GET 请求会被浏览器主动 cache,而 POST 不会,除非手动设置。
    • GET 请求只能进行 url 编码,而 POST 支持多种编码方式。
    • GET 请求参数会被完整保留在浏览器历史记录里,而 POST 中的参数不会被保留。
    • GET 请求在 URL 中传送的参数是有长度限制的,而 POST 没有。
    • 对参数的数据类型,GET 只接受 ASCII 字符,而 POST 没有限制。
    • GET 比 POST 更不安全,因为参数直接暴露在 URL 上,所以不能用来传递敏感信息。
    • GET 参数通过URL传递,POST 放在 Request body 中。
  • 正解 就一个区别:语义。GET 用于获取资源,POST 用于提交资源。
  • 装逼指南 参考链接: zhuanlan.zhihu.com/p/22536382

POST 为什么会发送两次请求?

当我们进行跨域请求时,尤其是使用 CORS(跨域资源共享)时,浏览器会在正式发送 POST 请求之前发送一个 OPTIONS 请求,这就是所谓的预检请求。这是为了确定服务器是否允许跨域请求。

预检请求:如果请求不满足简单请求的条件,浏览器会先发起一个预检请求,该请求使用 OPTIONS 方法,询问服务器是否允许来自该源的请求具有某些特定的 HTTP 方法和头部。预检请求的目的是保证安全,防止不受欢迎的跨源请求影响服务器数据。预检请求主要包含以下头部:

  • Access-Control-Request-Method:告诉服务器实际请求将使用的 HTTP 方法
  • Access-Control-Request-Headers:列出实际请求中将会额外设置的 HTTP 头部

什么是同源策略?

浏览器的同源策略是一种重要的安全机制,主要目的是防止不同源的文档之间相互访问。如果两个页面的协议、端口和主机完全相同,那么这两个页面具有相同的源。

  • 同源策略的限制
    • 限制 DOM 访问:不同源的页面之间不能通过 JavaScript 访问对方的 DOM 结构。例如,一个页面无法通过脚本读取另一个不同源页面的文档内容、修改其样式等。这可以防止恶意网站窃取用户在其他网站上的敏感信息。
    • 限制数据访问:不同源的页面之间不能通过 XMLHttpRequest、Fetch 等方式获取对方的资源。这确保了用户的隐私和数据安全,防止未经授权的数据访问。例如,一个恶意网站不能通过 Ajax 请求获取银行网站的用户账户信息。
    • 限制 Cookie、LocalStorage 等存储机制的访问:默认情况下,浏览器会阻止不同源的页面访问彼此的 Cookie、LocalStorage 等存储数据。这是为了保护用户的隐私和数据安全,防止恶意网站获取用户在其他网站上存储的敏感信息。
  • 同源策略的意义
    • 保护用户隐私和安全:同源策略有效地防止了恶意网站窃取用户在其他网站上的敏感信息,如登录凭证、个人资料等。它确保了用户在浏览网页时的隐私和安全。
    • 防止跨站攻击:同源策略可以阻止一些常见的跨站攻击,如跨站脚本攻击(XSS)和跨站请求伪造(CSRF)。通过限制不同源之间的访问,攻击者难以在用户的浏览器中执行恶意脚本或发起未经授权的请求。
    • 维护网站的独立性和完整性:每个网站都可以在一定程度上独立运行,不受其他不同源网站的干扰。这有助于维护网站的完整性和稳定性,确保用户获得可靠的浏览体验。

什么是跨域?解决跨域的方式有哪些?

  • 跨域 同源策略:浏览器出于安全方面的考虑,只允许于同域下的接口交互。 同域:必须协议相同,域名也相同。 否则就是跨域。

  • JSONP JSONP 是通过 script 标签加载数据的方式去获取数据然后当作 JS 代码执行。 提前在页面上声明一个函数,函数名通过接口传参的方式传给后台,后台解析到函数名后在原始数据上「包裹」这个函数名,发送给前端。换句话说,JSONP 需要对应接口的后端的配合才能实现。

      <script>
        function showData(result){
          console.log(result);
        }
      </script>
      <script src="http://api.jirengu.com/weather.php?callback=showData"></script>
    
  • CORS:跨源资源共享 作用:CORS 标准新增了一组 HTTP 首部字段,允许服务器声明哪些源站通过浏览器有权限访问哪些资源。

    注意:规范要求,对那些可能对服务器数据产生副作用的 HTTP 请求方法(特别是 GET 以外的 HTTP 请求,或者搭配某些 MIME 类型的 POST 请求),浏览器必须首先使用 OPTIONS 方法发起一个预检请求,从而获知服务端是否允许该跨源请求。

      var invocation = new XMLHttpRequest();
      var url = 'http://bar.other/resources/post-here/';
      var body = '<?xml version="1.0"?><person><name>Arun</name></person>';
    
      function callOtherDomain(){
        if(invocation) {
          invocation.open('POST', url, true);
          invocation.setRequestHeader('X-PINGOTHER', 'pingpong');
          invocation.setRequestHeader('Content-Type', 'application/xml');
          invocation.onreadystatechange = handler;
          invocation.send(body);
        }
      }
    

    除了被用户代理自动设置的首部字段(例如 Connection ,User-Agent)和在 Fetch 规范中定义为禁用首部名称的其他首部,以及自定义的请求首部字段,允许人为设置的字段有:

    • Accept:用来告知服务器可以处理的内容类型
    • Accept-Language:允许客户端声明它可以理解的自然语言
    • Content-Language:说明访问者希望采用的语言或语言组合
    • Content-Type:告诉客户端实际返回的内容的内容类型,值仅限于下列三者之一
      • text/plain
      • multipart/form-data
      • application/x-www-form-urlencoded
  • 服务器代理:通过配置服务器(如 Nginx)进行请求代理,将前端请求转发到目标服务器,再将响应返回给前端。在 Nginx 服务器配置中添加相应的跨域响应头,实现跨域访问控制。

  • postMessage:HTML5 提供的新特性,允许不同源的页面间通过 postMessage 方法安全地传递消息。

    • 基本用法
      • 发送消息:
      // 假设你有一个iframe或者一个被打开的窗口,其引用为targetWindow
      var targetWindow = document.getElementById('myiframe').contentWindow
      targetWindow.postMessage('Hello from the main page!', '*') // 也可以指定接收消息的确切源
      
      • 接收消息:
      // 添加事件监听器来接收消息
      window.addEventListener('message', function(event) {
        // 事件对象event中包含了接收到的消息
        var origin = event.origin // 发送消息页面的源
        var data = event.data // 发送的消息
        console.log('Received message:', data)
        // 检查origin以确保消息来自可信任的源
        if (origin === 'http://trustedsource.com') {
          // 处理接收到的消息
        }
      }, false)
      
    • 特点
      • 跨源通信:允许不同源的页面间进行通信,绕过浏览器的同源策略限制。
      • 安全性:通过检查 event.origin 属性,可以确定消息来自哪里,从而避免跨站脚本(XSS)攻击。
      • 灵活性:可以发送任何类型的数据,包括字符串、JSON对象等。
      • 双向通信:两个窗口可以互相发送消息,实现双向通信。
      • 不仅限于iframe:postMessage 可用于 window 对象、iframe 元素,甚至是使用 window.open 打开的新窗口。
    • 使用场景
      • 实现 iframe 与父页面之间的通信。
      • 在单页面应用(SPA)中,当使用 iframe 作为模块加载时,模块间通信。
      • 跨文档通信(Cross-document communication),比如当使用多个标签页或窗口打开同一应用的不同部分时。
    • 注意事项:
      • 使用 postMessage 时,应该始终检查 event.origin 来确认消息的来源是否可信,以防止安全漏洞。
      • 消息发送者可以指定目标源,如果不指定或使用通配符(*),则任何源都可以接收消息,这可能会带来安全风险。
      • 接收者应该在 message 事件的回调函数中处理消息,并在处理前验证消息的有效性和安全性。
  • WebSocket:使用WebSocket协议进行通信,它不受同源策略的限制,可以实现跨域数据传输。

HTTP 方法/请求类型

  • GET:获取资源
  • POST:传输资源
  • PUT:更新资源
  • DELETE:删除资源
  • HEAD:获取报文首部

HTTP 和 HTTPS 的区别?

HTTP 协议是超文本传输协议,信息是明文传输的;HTTPS 则是具有安全性的SSL加密传输协议。主要区别如下:

  • HTTPS 协议需要CA证书,费用较高;而 HTTP 协议不需要
  • 使用不同的连接方式,端口也不同,HTTP 协议端口是80,HTTPS 协议端口是443
  • HTTP 协议连接很简单,是无状态的;HTTPS 协议是有 SSL 和 HTTP 协议构建的可进行加密传输、身份认证的网络协议,比HTTP更加安全

HTTPS

HTTPS 是由 HTTP 协议和 SSL/TLS 协议一起合作完成的,在 HTTPS 数据传输的过程中,需要用 SSL/TLS 对数据进行加密和解密,需要用 HTTP 对加密后的数据进行传输。由于 HTTP 协议采用明文传输信息,很容易被窃听、篡改、劫持,而 HTTPS 增加的 TLS/SSL 层可以对身份进行验证、信息加密解密功能,避免这种问题发生。安全层的主要职责就是对发起的 HTTP 的数据进行加密解密操作。安全层依赖于三个算法:

  • 对称加密:用一个密钥加密数据,加密后也可以对其解密,存在安全风险,容易被劫持
  • 非对称加密:一个公钥一个私钥,用公钥加密的内容必须用私钥才能解开,同样私钥加密的内容只有公钥能解开,但如果公钥是一开始通过明文传输给浏览器的,也会造成劫持,用公钥解密数据
  • CA证书:向机构申请一份CA证书,里面包含网站信息和公钥A,服务端把证书传给浏览器,浏览器验证证书,拿到公钥A
  • 加密过程:安全层采用混合加密(对称加密 + 非对称加密)

SSL 的全称是 Secure Sockets Layer,即安全套接层协议,是为网络通信提供安全及数据完整性的一种安全协议。TLS 的全称是 Transport Layer Security,即安全传输层协议。LS/SSL的功能实现主要依赖三类基本算法:散列函数hash、对称加密、非对称加密。客户端使用非对称加密与服务器进行通信,实现身份的验证并协商对称加密使用的秘钥。

什么是 HTTPS 中间人攻击?如何预防?

在 HTTPS 传输过程中,存在非对称加密和对称加密两个加密过程,中间人攻击就是在非对称加密过程中,黑客传入自己的公钥,导致服务端用假的公钥加密了数据。

三次握手和四次挥手

字段理解:

  • seq:序列号,随机数 seq = x 每次连接都会携带一个自己的随机序列号
  • ack:确认号 ack = seq + 1 确认上一次收到了序列号,除了第一次连接都会携带
  • ACK:确认收到序列号 ACK = 1 除了第一次连接都会携带
  • SYN:发起一个新连接 SYN = 1
  • FIN:释放一个连接 FIN = 1

三次握手:建立一个 TCP 连接时,需要客户端和服务器总共发送3个包,主要作用是为了确认双方接受和发放能力是否正常。初始化时客户端处于 Closed 状态,服务端处于 Listen 状态。

  • 第一次握手:客户端给服务器发送一个 TCP 报文,表示向服务器请求连接
    • SYN = 1 建立一个新链接
    • seq = x 随机序列号
    • 此时客户端处理 SYN-SENT 状态
  • 第二次握手:服务器接收到 SYN 报文后,回复浏览器,表示同意连接
    • SYN = 1 同意建立一个新链接
    • ACK = 1 确认收到序列号
    • seq = y 随机序列号
    • ack = x + 1 确认收到序列号是x
    • 服务端进入 SYN-RCVD 阶段
  • 第三次握手:浏览器收到报文后回复服务器,表示收到回复
    • ACK = 1 确认收到序列号
    • seq = x + 1 上一次浏览器发送的 seq = x 基础上 +1
    • ack = y + 1 确认收到的序列号是服务器回复的 y
    • 服务器和浏览器同时进入 ESTABLISHED 状态
    • 此时双方连接已建立,开始传送数据 **为什么要三次握手呢?两次不行吗?**为了确认双方的接收能力和发送能力都正常。服务端在发送完 SYN 报文后,客户端需要回复一个 ACK 告诉服务端确认有效。

四次挥手:刚开始双方都处于 ESTABLISHED 状态。

  • 第一次挥手:向服务器发送报文
    • FIN = 1 关闭连接
    • seq = u 随机序列号
    • 浏览器进入 FIN-WAIT-1 半关闭阶段
  • 第二次挥手:服务器收到报文后,知道浏览器要断开链接,开始处理内部操作
    • ACK = 1 确认收到序列号
    • ack = u + 1 确认收到序列号为 u
    • seq = v 随机序列号
    • 服务器进入 CLOSE-WAIT 处理阶段
    • 浏览器进入 FIN-WAIT-2 阶段,等待服务器处理
  • 第三次挥手:服务器处理完毕,回复浏览器
    • FIN = 1 关闭连接
    • ACK = 1 确认收到序列号
    • ack = u + 1 确认收到序列号为 u
    • seq = w 随机序列号
    • 服务器进入 LAST-ACK 阶段,等待浏览器确认收到
  • 第四次挥手:浏览器收到服务器报文,回复服务器
    • ACK = 1
    • seq = u + 1
    • acl = w + 1
    • 浏览器进入 TIME-WAIT 阶段,此时 TCP 未释放掉,等待 2MSL(毫秒),等待服务器收到自己的报文,然后进入 CLOSED 阶段 **为什么是四次?**客户端发送 FIN 报文后,表示客户端当前没有数据需要处理,而不代表服务端没有数据需要处理。此时需要服务端回复 ACK 确认收到报文后,开始处理内部数据。当内部数据处理完后,再回复 FIN 可以关闭连接。

什么是缓存?为什么需要缓存?

将 Web 页面的静态资源(如img图片、css样式表、脚本等)保存到客户端(如浏览器)中,以便下次访问时可以直接从客户端获取,加快页面的加载速度,同时降低服务器压力。

缓存的优点:

  • 加快页面加载速度:当用户访问一个网站时,浏览器需要下载网站的所有资源才能显示页面。如果这些资源已经被缓存到客户端,那么用户再次访问该网站时,就可以直接从客户端获取缓存的资源,加快页面的加载速度。
  • 减少网络请求的数量和体积:如果每次访问 Web 页面时都需要从服务器下载所有资源,那么将会消耗大量的网络流量,影响用户体验。而如果将这些资源缓存到客户端,那么就可以在一定程度上减少网络传输,节约网络资源。
  • 减轻服务器压力:如果所有用户都从服务器下载资源,那么服务器的压力将会非常大。而如果将这些资源缓存到客户端,那么可以减轻服务器的负担,提高服务器的性能和稳定性。
  • 网络请求存在不稳定性,加剧了页面加载的不稳定性。

强缓存和协商缓存

  • 强缓存:浏览器不会像服务器发送任何请求,直接从本地缓存中读取文件并返回Status Code: 200 OK
    • 优先访问memory cache,其次是disk cache,最后是请求网络资源
    • 200 form memory cache:不访问服务器,一般已经加载过该资源且缓存在了内存当中,直接从内存中读取缓存。浏览器关闭后,数据将不存在(资源被释放掉了),再次打开相同的页面时,不会出现from memory cache
    • 200 from disk cache:不访问服务器,已经在之前的某个时间加载过该资源,直接从硬盘中读取缓存,关闭浏览器后,数据依然存在,此资源不会随着该页面的关闭而释放掉下次打开仍然会是from disk cache
  • 协商缓存:向服务器发送请求,服务器会根据这个请求的请求头的一些参数来判断是否命中协商缓存,如果命中,则返回 304 状态码并带上新的响应头通知浏览器从缓存中读取资源。

强缓存和协商缓存的 header 参数

  • 强缓存
    • Expires:过期时间,如果设置了时间,则浏览器会在设置的时间内直接读取缓存,不再请求
    • Cache-Control:当值设为max-age=300时,则代表在这个请求正确返回时间(浏览器也会记录下来)的5分钟内再次加载资源,就会命中强缓存
    • Cache-control是 HTTP1.1 的头字段,Expires是 HTTP1.0 的头字段,如果同时存在,Cache-control会覆盖Expires,建议两个都写
  • 协商缓存
    • Etag/If-None-Match
      • Etag 是属于 HTTP1.1 属性,它是由服务器(Apache或者其他工具)生成返回给前端,用来帮助服务器控制Web端的缓存验证
      • If-None-Match:当资源过期时,浏览器发现响应头里有Etag,则再次像服务器请求时带上请求头If-None-Match(值是Etag的值)。服务器收到请求进行比对,有变化返回 200,没变化则返回 304
    • Last-Modified/If-Modified-Since
      • Last-Modified:服务器向浏览器发送最后的修改时间
      • If-Modified-Since:当资源过期时(浏览器判断Cache-Control标识的max-age过期),发现响应头具有Last-Modified声明,则再次向服务器请求时带上头If-Modified-Since,表示请求时间。服务器收到请求后发现有If-Modified-Since则与Last-Modified被请求资源的最后修改时间进行对比,若最后修改时间较新(大),说明资源又被改过,则返回最新资源,HTTP 200 OK;若最后修改时间较旧(小),说明资源无新修改,响应HTTP 304走缓存
    • 注意:
      • Last-Modified/If-Modified-Since的时间精度是秒,而Etag可以更精确
      • Etag优先级是高于Last-Modified的,所以服务器会优先验证Etag
      • Last-Modified/If-Modified-Since是 HTTP1.0 的头字段

Cookie vs LocalStorage vs SessionStorage vs Session

  • Cookie vs LocalStorage
    • 主要区别是 Cookie 会被发送到服务器,而 LocalStorage 不会。
    • Cookie 一般最大 4k,LocalStorage 可以用 5Mb 甚至 10Mb(各浏览器不同)。
    • Cookie 包含用户信息,LocalStorage 包含的是不重要的信息。
  • LocalStorage vs SessionStorage
    • LocalStorage 一般不会自动过期(除非用户手动清除),而 SessionStorage 在回话结束时过期(如关闭浏览器)。
  • Cookie vs Session
    • Cookie 存在浏览器的文件里,Session 存在服务器的文件里。
    • Session 是基于 Cookie 实现的,具体做法就是把 SessionID 存在 Cookie 里。

什么是 Cookie?

  • HTTP 是无状态的协议(对于事务处理没有记忆能力,每次客户端和服务端会话完成时,服务端不会保存任何会话信息):每个请求都是完全独立的,服务端无法确认当前访问者的身份信息,无法分辨上一次的请求发送者和这一次的发送者是不是同一个人。所以服务器与浏览器为了进行会话跟踪(知道是谁在访问我),就必须主动的去维护一个状态,这个状态用于告知服务端前后两个请求是否来自同一浏览器。而这个状态需要通过 cookie 或者 session 去实现。
  • cookie 存储在客户端:cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,它会在浏览器下次向同一服务器再发起请求时被携带并发送到服务器上。
  • cookie 是不可跨域的:每个 cookie 都会绑定单一的域名,无法在别的域名下获取使用,一级域名和二级域名之间是允许共享使用的(靠的是 domain)。

服务端可不可以修改 Cookie 值?

服务端可以通过响应头修改 Cookie 值。当客户端向服务端发送请求时,服务端可以在响应头部中设置Set - Cookie字段来修改 Cookie 的值。

什么是 Session?

  • session 是另一种记录服务器和客户端会话状态的机制。
  • session 是基于 cookie 实现的,session 存储在服务器端,sessionId 会被存储到客户端的cookie 中。

session 认证流程:

  1. 用户第一次请求服务器的时候,服务器根据用户提交的相关信息,创建对应的 Session。
  2. 请求返回时将此 Session 的唯一标识信息 SessionID 返回给浏览器。
  3. 浏览器接收到服务器返回的 SessionID 信息后,会将此信息存入到 Cookie 中,同时 Cookie 记录此 SessionID 属于哪个域名。
  4. 当用户第二次访问服务器的时候,请求会自动判断此域名下是否存在 Cookie 信息,如果存在自动将 Cookie 信息也发送给服务端,服务端会从 Cookie 中获取 SessionID,再根据 SessionID 查找对应的 Session 信息,如果没有找到说明用户没有登录或者登录失效,如果找到 Session 证明用户已经登录可执行后面操作。

Cookie 和 Session 的区别

  • 安全性:Session 比 Cookie 安全,Session 是存储在服务器端的,Cookie 是存储在客户端的。
  • 存取值的类型不同:Cookie 只支持存字符串数据,想要设置其他类型的数据,需要将其转换成字符串,Session 可以存任意数据类型。
  • 有效期不同:Cookie 可设置为长时间保持,比如我们经常使用的默认登录功能,Session 一般失效时间较短,客户端关闭(默认情况下)或者 Session 超时都会失效。
  • 存储大小不同:单个 Cookie 保存的数据不能超过 4K,Session 可存储数据远高于 Cookie,但是当访问量过多,会占用过多的服务器资源。

什么是 Token(Access Token)?

  • 访问资源接口(API)时所需要的资源凭证
  • 简单 token 的组成: uid(用户唯一的身份标识)、time(当前时间的时间戳)、sign(签名,token 的前几位以哈希算法压缩成的一定长度的十六进制字符串)
  • 特点:
    • 服务端无状态化、可扩展性好
    • 支持移动端设备
    • 安全
    • 支持跨程序调用

token 的身份验证流程:

  1. 客户端使用用户名跟密码请求登录
  2. 服务端收到请求,去验证用户名与密码
  3. 验证成功后,服务端会签发一个 token 并把这个 token 发送给客户端
  4. 客户端收到 token 以后,会把它存储起来,比如放在 cookie 里或者 localStorage 里
  5. 客户端每次向服务端请求资源的时候需要带着服务端签发的 token
  6. 服务端收到请求,然后去验证客户端请求里面带着的 token ,如果验证成功,就向客户端返回请求的数据

注意:

  • 每一次请求都需要携带 token,需要把 token 放到 HTTP 的 Header 里
  • 基于 token 的用户认证是一种服务端无状态的认证方式,服务端不用存放 token 数据。用解析 token 的计算时间换取 session 的存储空间,从而减轻服务器的压力,减少频繁的查询数据库
  • token 完全由应用管理,所以它可以避开同源策略

Refresh Token:

  • refresh token 是专用于刷新 access token 的 token。如果没有 refresh token,也可以刷新 access token,但每次刷新都要用户输入登录用户名与密码,会很麻烦。有了 refresh token,可以减少这个麻烦,客户端直接用 refresh token 去更新 access token,无需用户进行额外的操作。
  • Access Token 的有效期比较短,当 Access Token 由于过期而失效时,使用 Refresh Token 就可以获取到新的 Token,如果 Refresh Token 也失效了,用户就只能重新登录了。
  • Refresh Token 及过期时间是存储在服务器的数据库中,只有在申请新的 Access Token 时才会验证,不会对业务接口响应时间造成影响,也不需要向 Session 一样一直保持在内存中以应对大量的请求。

Token 和 Session 的区别

  • Session 是一种记录服务器和客户端会话状态的机制,使服务端有状态化,可以记录会话信息。而 Token 是令牌,访问资源接口(API)时所需要的资源凭证。Token 使服务端无状态化,不会存储会话信息。
  • Session 和 Token 并不矛盾,作为身份认证 Token 安全性比 Session 好,因为每一个请求都有签名还能防止监听以及重放攻击,而 Session 就必须依赖链路层来保障通讯安全了。如果你需要实现有状态的会话,仍然可以增加 Session 来在服务器端保存一些状态。
  • 所谓 Session 认证只是简单的把 User 信息存储到 Session 里,因为 SessionID 的不可预测性,暂且认为是安全的。而 Token ,如果指的是 OAuth Token 或类似的机制的话,提供的是 认证 和 授权 ,认证是针对用户,授权是针对 App 。其目的是让某 App 有权利访问某用户的信息。这里的 Token 是唯一的。不可以转移到其它 App上,也不可以转到其它用户上。Session 只提供一种简单的认证,即只要有此 SessionID ,即认为有此 User 的全部权利。是需要严格保密的,这个数据应该只保存在站方,不应该共享给其它网站或者第三方 App。

什么是 JWT?为什么要用 JWT?

JWT(JSON Web Token)是一种开放标准(RFC 7519),用于在网络上安全传输信息的简洁、自包含的方式。它通常被用于身份验证和授权机制。JWT 由三部分组成:头部(Header)、载荷(Payload)和签名(Signature):

  • 头部(Header):包含了关于生成该 JWT 的信息以及所使用的算法类型。
  • 载荷(Payload):包含了要传递的数据,例如身份信息和其他附属数据。JWT 官方规定了 7 个字段:
    • iss (Issuer):签发者
    • sub (Subject):主题
    • aud (Audience):接收者
    • exp (Expiration time):过期时间
    • nbf (Not Before):生效时间
    • iat (Issued At):签发时间
    • jti (JWT ID):编号
  • 签名(Signature):使用密钥对头部和载荷进行签名,以验证其完整性。

JWT 相较于传统的基于 Session 的认证机制,具有以下优势:

  • 无需服务器存储状态 传统的基于会话的认证机制需要服务器在会话中存储用户的状态信息,包括用户的登录状态、权限等。而使用 JWT,服务器无需存储任何会话状态信息,所有的认证和授权信息都包含在 JWT 中,使得系统可以更容易地进行水平扩展。
  • 跨域支持 由于 JWT 包含了完整的认证和授权信息,因此可以轻松地在多个域之间进行传递和使用,实现跨域授权。
  • 适应微服务架构 在微服务架构中,很多服务是独立部署并且可以横向扩展的,这就需要保证认证和授权的无状态性。使用 JWT 可以满足这种需求,每次请求携带 JWT 即可实现认证和授权。
  • 自包含 JWT 包含了认证和授权信息,以及其他自定义的声明,这些信息都被编码在 JWT 中,在服务端解码后使用。JWT 的自包含性减少了对服务端资源的依赖,并提供了统一的安全机制。
  • 扩展性 JWT 可以被扩展和定制,可以按照需求添加自定义的声明和数据,灵活性更高。

实现原理:JWT 本质是将秘钥存放在服务器端,并通过某种加密手段进行加密和验证的机制。加密签名=某加密算法(header+payload+服务器端私钥),因为服务端私钥别人不能获取,所以 JWT 能保证自身其安全性。

Token 和 JWT 的区别

相同:

  • 都是访问资源的令牌
  • 都可以记录用户的信息
  • 都是使服务端无状态化
  • 都是只有验证成功后,客户端才能访问服务端上受保护的资源

区别:

  • Token:服务端验证客户端发送过来的 Token 时,还需要查询数据库获取用户信息,然后验证 Token 是否有效
  • JWT:将 Token 和 Payload 加密后存储于客户端,服务端只需要使用密钥解密进行校验(校验也是 JWT 自己实现的)即可,不需要查询或者减少查询数据库,因为 JWT 自包含了用户信息和加密的数据

当在浏览器中输入 URL 并且按下回车之后发生了什么?

  1. URL 解析 浏览器首先会判断 URL 是否符合规范,判断输入的内容是一个 URL 还是搜索关键字。如果是 URL,会把不完整的 URL 合成完整的 URL。如果是搜索关键字,会将其拼接到默认搜索引擎的参数部分去搜索。
  2. 浏览器判断缓存,判断请求都资源是否有缓存,如果有就不需要向服务器发送新的请求,如「强缓存、协商缓存」
  3. DNS 解析 如果没有成功使用本地缓存,则需要发起网络请求了。首先要做的是 DNS 解析。会依次搜索:
    1. 浏览器的DNS缓存
    2. 操作系统的DNS缓存
    3. 路由器的DNS缓存
    4. 向服务商的DNS服务器查询
    5. 向全球13台根域名服务器查询
  4. 建立 TCP 连接(三次握手)
  5. 发送 HTTP 请求
  6. 服务器处理,返回数据
  7. 浏览器解析渲染页面:
    1. 构建 DOM 树
    2. 样式计算
    3. 布局定位
    4. 图层分层
    5. 图层绘制
    6. 显示
  8. TCP连接关闭(四次挥手)

HTTP/1 和 HTTP/2 有什么区别?

  • 多路复用
    • HTTP/1:每次请求都需要建立单独的连接,多个请求之间是串行的,会导致请求阻塞,影响页面加载速度。
    • HTTP/2:支持多路复用,多个请求可以在同一个连接上并行发送和接收,消除了队首阻塞问题,大大提高了性能。
  • 二进制分帧
    • HTTP/1:以文本形式传输数据,解析相对较慢。
    • HTTP/2:采用二进制格式传输数据,将报文分成更小的帧进行传输,解析速度更快,并且更易于优化和扩展。
  • 重复头部问题
    • HTTP/1:每次请求和响应都包含大量的重复头部信息,增加了传输开销。
    • HTTP/2:使用 HPACK 算法对头部进行压缩,减少了头部的大小,降低了传输成本。
  • 服务器主动推送资源
    • HTTP/1:客户端必须发起请求才能获取资源。
    • HTTP/2:服务器可以主动向客户端推送资源,例如在客户端请求一个页面时,服务器可以同时推送该页面所需的 CSS、JavaScript 等资源,减少了客户端的请求次数,加快页面加载速度。
  • 资源优先级调整
    • HTTP/1:没有明确的资源优先级设置,浏览器只能按照请求的顺序依次处理。
    • HTTP/2:允许为每个请求设置优先级,服务器可以根据优先级来分配资源和处理请求,确保重要的资源先被传输和处理。

更多的区别

  • 流控制方面:
    • HTTP/1:没有明确的流控制机制,主要依靠传输层的 TCP 连接自带的一些流量控制功能,但这种控制相对较为基础,且每个 TCP 连接需要独立的流控制机制。例如,在 HTTP/1.1 中,当连接建立时,两端通过系统默认机制建立缓冲区,并通过 ACK 报文来通知对方接收窗口大小,但这种方式对于复杂的网络环境和多样的应用场景,流控制的灵活性和精细度不够。
    • HTTP/2:引入了流控制机制,允许接收方通知发送方的发送速率,从而防止发送方发送速度过快导致接收方无法及时处理的问题。这种流控制是在应用层实现的,并且是基于每个流进行的,因此可以更精细地控制数据的传输速度和流量,提高了网络资源的利用率和数据传输的稳定性。
  • 优先级和依赖方面:
    • HTTP/1:不支持明确的请求优先级和依赖关系,所有的请求都是按照发送的顺序依次处理。在一些复杂的网页应用中,可能存在多个资源请求,但是 HTTP/1 无法指定哪个请求更为重要或者哪些请求之间存在依赖关系,这可能导致资源加载的顺序不够优化,影响页面的加载速度和性能。
    • HTTP/2:引入了请求的优先级和依赖关系,允许客户端为请求分配优先级,确保重要请求能够优先得到处理。例如,对于一个网页,页面的 HTML 内容可能比一些图片或广告资源更为重要,通过设置优先级,可以让浏览器先接收和处理 HTML 内容,然后再处理其他资源,这样有助于更有效地利用网络资源,提高页面的加载速度和用户体验。
  • 协议标识和安全性方面:
    • HTTP/1:使用明文标识,容易被中间人攻击修改。在网络传输过程中,HTTP/1 的请求和响应信息都是以明文形式传输的,这使得攻击者可以轻易地截取、篡改或伪造 HTTP 报文,存在较大的安全风险。
    • HTTP/2:通过 TLS(Transport Layer Security)实现的加密传输,增加了安全性,防止了中间人攻击。TLS 协议对数据进行加密和认证,确保了数据在传输过程中的保密性、完整性和真实性,大大提高了 HTTP/2 的安全性。
  • 适用场景方面:
    • HTTP/1:具有极高的兼容性,几乎被所有现有的 Web 浏览器、服务器和中间设备所支持,因此适用于几乎所有需要 HTTP 通信的场景,尤其是在并发请求量不高的场景下,性能表现良好,能够满足一般 Web 应用的基本需求,如个人博客、小型企业网站等。
    • HTTP/2:更适用于高并发场景,如大型电子商务网站、社交媒体平台等。其多路复用、二进制分帧和头部压缩技术能够减少数据传输的大小,提高传输效率,对于需要传输大量数据的场景也非常适用。同时,其低延迟特性使其适用于实时通信应用,如在线游戏、实时视频聊天等对数据传输的实时性和可靠性有较高要求的场景。

HTTP/2 相对于 HTTP/1 的优势体现在哪些方面?

  • 性能大幅提升
    • 多路复用:
      • 在 HTTP/1 中,多个请求必须依次排队等待响应,即使不同请求的资源可以并行获取,也会因为只能逐个处理而导致延迟。例如,当一个网页包含多个图片、脚本和样式表时,每个资源都需要单独建立连接进行请求,这不仅增加了服务器的负担,还会因为请求的串行化而延长页面加载时间。
      • HTTP/2 的多路复用允许同时在一个连接上发送多个请求和响应,无需等待一个请求完成后再发送下一个。这就像在一条高速公路上同时有多辆车并行行驶,大大提高了传输效率,减少了页面加载时间。
    • 二进制分帧:
      • HTTP/1 使用文本格式传输数据,解析相对较慢,并且容易出现错误。例如,文本格式的报文在传输过程中可能因为字符编码问题或者格式不规范而导致解析错误,需要进行额外的错误处理。
      • HTTP/2 采用二进制格式分帧传输,使得数据更加紧凑,解析速度更快,并且减少了错误的发生概率。二进制格式的帧可以被高效地处理和传输,提高了系统的整体性能。
  • 头部压缩,更高效,减少传输开销
    • HTTP/1 中,每次请求和响应都包含大量重复的头部信息,尤其是在多个请求来自同一客户端或指向同一服务器时,这些重复的头部会占用大量的带宽。例如,常见的头部字段如 User-Agent、Accept、Cookie 等在多个请求中可能是相同的,但在 HTTP/1 中每次都需要完整传输。
    • HTTP/2 使用 HPACK 算法对头部进行压缩,消除了重复的头部信息,大大减少了头部的大小。通过在连接建立时维护一个动态的头部字典,只传输头部的变化部分,从而降低了传输开销。
  • 服务器推送资源,优化页面加载
    • 在 HTTP/1 中,客户端必须发起请求才能获取资源。例如,当用户访问一个网页时,浏览器首先解析 HTML 文件,然后根据 HTML 中的链接逐个发起请求获取 CSS、JavaScript、图片等资源,这个过程会导致明显的延迟,尤其是在网络状况不佳或者资源较多的情况下。
    • HTTP/2 的服务器推送允许服务器主动向客户端推送资源,当客户端请求一个页面时,服务器可以预测客户端可能需要的其他资源,并在响应页面请求的同时将这些资源推送给客户端。这样,客户端在接收到页面的同时就可以开始加载推送的资源,大大缩短了页面加载时间,提高了用户体验。
  • 更好的流控制,能精细控制数据传输
    • TTP/1 缺乏有效的流控制机制,主要依赖于传输层的 TCP 流控制,而 TCP 的流控制是针对整个连接的,无法对单个请求进行精细控制。例如,当一个大文件的下载占用了大部分带宽时,其他请求可能会因为带宽不足而受到严重影响,导致页面加载缓慢或者响应延迟。
    • HTTP/2 的流控制机制允许接收方根据自己的处理能力来控制每个流的发送速度,确保不会因为某个流的数据发送过快而导致接收方无法及时处理。这样可以更好地平衡不同请求之间的带宽分配,提高系统的稳定性和性能。
  • 安全性增强
    • HTTP/1 通常以明文方式传输数据,容易被中间人攻击窃取或篡改信息。例如,在未使用加密的 HTTP/1 连接中,用户的登录凭证、个人信息等敏感数据在网络中传输时可能被攻击者拦截并获取。
    • HTTP/2 通常与加密协议(如 TLS/SSL)一起使用,确保数据在传输过程中的保密性和完整性。加密后的通信可以防止攻击者窃取敏感信息,提高了系统的安全性。

HTTP 请求报文

  • 请求行
    • 请求类型
    • 要访问的资源
    • HTTP 协议版本号
  • 请求头
    • 用来说明服务器要使用的附加信息(一些键值对)
    • 例如:User-Agent、 Accept、Content-Type、Connection
  • 空行
    • 分割请求头与请求体
  • 请求体
    • 可以添加任意的其他数据

HTTP 响应报文

  • 状态行
    • 状态码
    • 状态消息
    • HTTP 协议版本号
  • 消息报头
    • 说明客户端要使用的一些附加信息
    • 例如:Content-Type、charset、响应的时间
  • 响应正文
    • 返回给客户端的文本信息

HTTP 协议的特点

  • 无连接 限制每次连接只处理一个请求。
  • 无状态 协议对于事务处理没有记忆能力。
  • 简单快速 客户向服务器请求服务时,只需传送请求方法和路径。
  • 灵活 HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记。

WebSocket

什么是 WebSocket?

WebSocket 是一种在单个 TCP 连接上进行全双工通信的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数据。在 WebSocket API 中,浏览器和服务器只需要完成一次握手,两者之间就直接可以创建持久性的连接,并进行双向数据传输。

与 HTTP 协议一样,WebSocket 协议也需要通过已建立的TCP连接来传输数据。具体实现上是通过 HTTP 协议建立通道,然后在此基础上用真正的 WebSocket 协议进行通信,所以 WebSocket 协议和 HTTP 协议是有一定的交叉关系的。

与传统的 HTTP 协议相比,Websocket 协议的优缺点分别是什么?

  • Websocket 协议的优点
    • 实时性高
      • Websocket 建立连接后,双方可以随时发送数据,实现真正的实时通信。而传统 HTTP 协议需要客户端发起请求后服务器才能响应,在实时性要求高的场景下会有明显的延迟。
      • 例如在在线游戏、金融交易行情显示、实时监控等场景中,Websocket 可以及时推送数据,让用户获得最新的信息。
    • 减少网络开销
      • Websocket 只需要在建立连接时进行一次握手,之后就可以持续双向通信,避免了传统 HTTP 协议中频繁的请求和响应带来的网络开销。
      • 对于需要频繁通信的应用,如即时通讯软件,Websocket 可以大大减少网络流量,提高通信效率。
    • 服务器推送
      • Websocket 支持服务器主动向客户端推送数据,而 HTTP 协议通常只能由客户端发起请求。这使得服务器可以在有新数据时立即推送给客户端,无需客户端不断轮询。
      • 比如在社交网络中,当有新的消息或通知时,服务器可以通过 Websocket 直接推送给用户,提高用户体验。
  • Websocket 协议的缺点
    • 服务器资源消耗
      • Websocket 建立的是持久连接,服务器需要一直维护这些连接,占用一定的内存和 CPU 资源。相比之下,HTTP 协议在请求处理完成后就可以释放资源。
      • 在高并发场景下,大量的 Websocket 连接可能会导致服务器负载过高,甚至出现性能问题。
    • 兼容性问题
      • 虽然现代浏览器对 Websocket 的支持越来越广泛,但仍有一些老旧的浏览器不支持该协议。而 HTTP 协议几乎被所有的浏览器和设备支持。
      • 这就使得在开发应用时,需要考虑到兼容性问题,可能需要为不支持 Websocket 的客户端提供备用方案。
    • 安全风险
      • Websocket 连接可以在不同的源之间建立,这增加了跨站攻击的风险。同时,由于数据是双向传输的,安全防护难度也相对较大。
      • 相比之下,HTTP 协议经过多年的发展,有较为成熟的安全机制,如 HTTPS 可以提供加密和认证。
    • 复杂性增加
      • 使用 Websocket 进行开发需要掌握一定的网络编程知识,相比传统的 HTTP 开发,复杂度有所增加。
      • 对于一些简单的应用场景,可能使用 HTTP 协议更加方便快捷。

Websocket 有什么优缺点?

优点:

  • 实时性高:能够实现双向实时通信,服务器可以主动向客户端推送数据,无需客户端频繁轮询,大大降低了网络开销和延迟,让数据的更新更加及时。
  • 高效性:相比传统的 HTTP 请求/响应模式,减少了连接建立和关闭的开销。一旦建立连接,就可以持续进行数据传输,提高了通信效率。
  • 兼容性好:大多数现代浏览器都支持 Websocket 协议,方便在不同的平台和设备上使用,具有广泛的适用性。
  • 轻量级:协议相对简单,数据传输更加高效,占用的网络带宽较少,尤其适合对实时性要求高且网络资源有限的场景。

缺点:

  • 服务器资源消耗
    • 长连接维护成本高:
      • Websocket 建立的是持久连接,服务器需要一直维护这些连接,这会占用一定的内存和 CPU 资源。随着连接数量的增加,服务器的负担也会相应加重。如果连接数量过多,可能会导致服务器性能下降,甚至出现崩溃的情况。
      • 例如,在一个高并发的实时通信系统中,服务器可能需要同时处理数千个甚至更多的 Websocket 连接,这对服务器的硬件资源和软件架构都是一个很大的挑战。
    • 资源分配问题:
      • 由于 Websocket 连接可能长时间保持活跃状态,服务器需要为每个连接分配一定的资源。如果某些连接长时间没有数据传输,但仍然占用着资源,就会造成资源的浪费。
      • 比如,在一个在线游戏服务器中,一些玩家可能在游戏过程中暂时离开,但他们的 Websocket 连接仍然保持着,这就占用了服务器的资源,而这些资源本可以分配给其他活跃的玩家。
  • 兼容性问题
    • 浏览器兼容性:
      • 不同的浏览器对 Websocket 的支持程度有所不同。一些老旧的浏览器可能不支持 Websocket 协议,或者只支持部分功能。这就使得在开发 Web 应用时,需要考虑到兼容性问题,可能需要为不支持 Websocket 的浏览器提供备用方案。
      • 例如,IE 浏览器在较低版本中对 Websocket 的支持就比较有限,开发人员可能需要使用 Flash 或者其他技术来实现类似的实时通信功能,但这样会增加开发的复杂性和维护成本。
    • 服务器端兼容性:
      • 不同的服务器端技术对 Websocket 的支持也不尽相同。有些服务器框架可能对 Websocket 的支持不够完善,或者需要额外的配置和插件才能使用。这就给开发人员在选择服务器技术时带来了一定的困扰。
      • 比如,在使用某些老旧的服务器框架时,可能需要手动安装 Websocket 扩展才能实现实时通信功能,而这个过程可能会比较复杂,并且可能会引入一些潜在的兼容性问题。
  • 安全风险
    • 跨站攻击风险:
      • Websocket 连接可以在不同的源之间建立,这就增加了跨站攻击的风险。攻击者可以利用 Websocket 连接来发送恶意数据,或者进行其他攻击行为。
      • 例如,攻击者可以通过在一个恶意网站上建立 Websocket 连接,向用户访问的其他网站发送恶意请求,从而窃取用户的敏感信息或者进行其他恶意操作。
    • 数据安全问题:
      • 由于 Websocket 传输的数据是实时的,并且可能没有经过充分的加密和验证,这就使得数据在传输过程中容易被窃取或篡改。特别是在一些对数据安全要求较高的应用场景中,如金融交易、医疗信息系统等,需要采取更加严格的安全措施来保护数据的安全。
      • 比如,在一个在线银行系统中,如果使用 Websocket 进行实时交易通知,就需要确保数据在传输过程中是加密的,并且只有合法的用户才能接收到这些通知,以防止攻击者窃取用户的交易信息。
  • 复杂性增加
    • 开发复杂性:
      • 使用 Websocket 进行开发需要掌握一定的网络编程知识和技术,特别是对于一些复杂的应用场景,如多人实时协作、在线游戏等,开发难度会比较大。
      • 例如,在开发一个多人在线游戏时,需要处理大量的实时数据传输和同步问题,这就需要开发人员对 Websocket 的底层原理和通信机制有深入的了解,并且需要使用一些高级的编程技术和算法来实现高效的实时通信。
    • 调试复杂性:
      • 由于 Websocket 是一种双向通信协议,调试起来可能会比较困难。特别是在出现问题时,很难确定是客户端还是服务器端的问题,需要进行复杂的调试和分析。
      • 比如,当一个 Websocket 连接出现故障时,开发人员需要检查客户端和服务器端的代码,查看网络连接状态,分析数据传输过程等,这就需要花费较多的时间和精力来定位和解决问题。

WebSocket 的连接过程

  1. 客户端发起连接请求:客户端(通常是浏览器)通过创建一个 WebSocket 对象来发起连接请求。
  2. 握手阶段
  • 客户端发送一个 HTTP 请求到服务器,请求升级到 WebSocket 协议。这个请求包含一些特殊的头部信息,如Upgrade、Connection 和 Sec-WebSocket-Key 等。
  • 服务器接收到这个请求后,如果支持 WebSocket 协议,会返回一个特殊的 HTTP 响应,同意升级到 WebSocket 协议。这个响应也包含一些特殊的头部信息,如 Upgrade、Connection 和 Sec-WebSocket-Accept 等。
  1. 连接建立:一旦客户端收到服务器的响应,WebSocket 连接就建立成功了。此时,客户端和服务器可以通过这个连接双向通信,发送和接收消息。

WebSocket 与 HTTP 的区别

  • 相同点:都是一样基于 TCP 的,都是可靠性传输协议。都是应用层协议。
  • 联系:WebSocket 在建立握手时,数据是通过 HTTP 传输的。但是建立之后,在真正传输时候是不需要 HTTP 协议的。
  • 区别:
    • WebSocket 是双向通信协议,模拟 Socket 协议,可以双向发送或接受信息,而 HTTP 是单向的
    • WebSocket 是需要浏览器和服务器握手进行建立连接的,而 HTTP 是浏览器发起向服务器的连接

Websocket 断线重连

如何判断在线离线?

  1. 当客户端第一次发送请求至服务端时会携带唯一标识、以及时间戳,服务端到数据库或者缓存去查询改请求的唯一标识,如果不存在就存入db或者缓存中
  2. 第二次客户端定时再次发送请求依旧携带唯一标识、以及时间戳,服务端到数据库或者缓存去查询改请求的唯一标识,如果存在就把上次的时间戳拿取出来,使用当前时间戳减去上次的时间,得出的毫秒秒数判断是否大于指定的时间,若小于的话就是在线,否则就是离线

如何解决断线问题?

  • 原因1:Websocket 超时没有消息自动断开连接 这时候就需要知道服务端设置的超时时长是多少,在小于超时时间内发送心跳包,有两种方案:一种是客户端主动发送上行心跳包,另一种方案是服务端主动发送下行心跳包。关于客户端也就是前端如何实现心跳包(心跳检测步骤):
    • 客户端每隔一个时间间隔发生一个探测包给服务器
    • 客户端发包时启动一个超时定时器
    • 服务器端接收到检测包,应该回应一个包
    • 如果客户机收到服务器的应答包,则说明服务器正常,删除超时定时器
    • 如果客户端的超时定时器超时,依然没有收到应答包,则说明服务器挂了
  • 原因2:服务端出现中断 当服务端宕机了,客户端需要断开连接,通过 onclose 关闭连接,服务端再次上线时则需要清除之间存的数据,若不清除会造成只要请求到服务端的都会被视为离线。

心跳包机制补充:

  • 定义:心跳包像心跳一样每隔固定时间发一次,以此来告诉服务器,这个客户端还活着。事实上是为了保持长连接,至于这个包的内容,是没有什么特别规定的,不过一般都是很小的包,或者只包含包头的一个空包。下一个定时器,在一定时间间隔下发送一个空包给客户端,然后客户端反馈一个同样的空包回来,服务器如果在一定时间内收不到客户端发送过来的反馈包,那就只有认定说掉线了。
  • 为什么需要:在长连接下,有可能很长一段时间都没有数据往来。理论上说,这个连接是一直保持连接的,但是实际情况中,如果中间节点出现什么故障是很难知道的。更严重的是,有的节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉。在这个时候就需要心跳包来维持长连接、保活。

除了 Websocket,还有哪些能实现双端通信的方式?

  • 长轮询(Long Polling)
    • 原理:客户端向服务器发送请求,服务器保持连接打开,直到有新数据可用或者超时。如果有数据,服务器立即响应并关闭连接;如果没有数据,服务器在一段时间后返回空响应,客户端随即再次发起请求。通过不断重复这个过程,实现近似实时的通信。
    • 优点:
      • 相对简单,易于实现,不需要新的浏览器特性支持。
      • 在一些情况下可以提供较好的实时性,尤其是对于不支持 WebSocket 的旧版浏览器。
    • 缺点:
      • 由于需要不断发送请求,会消耗较多的服务器资源和网络带宽。
      • 实时性不如 WebSocket,存在一定的延迟。
  • 服务器发送事件(Server-Sent Events,SSE)
    • 工作原理
      1. 客户端发起连接:
      • 客户端(通常是网页中的 JavaScript)使用标准的 HTTP 请求向服务器请求一个特定的资源,通常是一个以.event-stream为后缀的 URL。
      • 这个请求与普通的 HTTP 请求类似,但在请求头中可以包含一些特定的参数,例如设置连接保持打开的参数。
      1. 服务器响应:
      • 服务器接收到请求后,保持连接打开,并不断向客户端发送事件流。
      • 事件流是一种文本格式的数据,由一系列以特定格式组织的事件组成。每个事件包含事件类型、数据和可选的标识符等信息。
      • 服务器通过不断写入数据到连接来发送事件,数据以 UTF-8 编码的文本形式发送。
      1. 客户端接收和处理:
      • 客户端的 JavaScript 代码使用 EventSource 对象来接收服务器发送的事件流。
      • 当有新的事件到达时,EventSource 对象会触发相应的事件处理函数,开发者可以在这些处理函数中处理接收到的数据,并更新网页的内容。
    • 特点
      • 单向通信:
        • SSE 是一种单向的通信方式,即只能从服务器向客户端发送数据。客户端不能直接向服务器发送数据,除非通过另外的 HTTP 请求。
      • 轻量级:
        • 基于 HTTP 协议,不需要额外的插件或库,使用简单。
        • 事件流的数据格式相对简单,易于解析和处理。
      • 自动重连:
        • 如果连接中断,EventSource对象会自动尝试重新连接服务器,确保数据的持续接收。
        • 可以设置重连的时间间隔和重试次数等参数。
      • 跨域支持:
        • 与其他基于 HTTP 的技术一样,SSE 支持跨域请求,可以从不同的域名接收事件流。但需要服务器在响应头中设置适当的跨域访问控制(CORS)参数。
    • 应用场景
      • 实时通知:例如,在社交媒体平台上,当有新的消息、通知或动态时,服务器可以使用 SSE 向用户的网页推送实时通知,而无需用户不断刷新页面。
      • 股票行情更新:金融交易平台可以使用 SSE 向用户实时推送股票价格、指数等行情数据,让用户及时了解市场动态。
      • 日志监控:系统管理员可以通过 SSE 实时监控服务器的日志文件,当有新的日志条目产生时,立即在网页上显示,以便及时发现和处理问题。

SSE 与 WebSocket 的区别是什么?

  • 通信方式
    • SSE 是单向通信:
      • 服务器可以向客户端发送数据,但客户端不能直接向服务器发送数据,除非通过另外的 HTTP 请求。
      • 主要用于服务器向客户端推送实时数据,如新闻更新、股票行情等。
    • WebSocket 是双向通信:
      • 客户端和服务器可以随时互相发送数据。
      • 适用于需要实时交互的场景,如在线游戏、聊天应用等。
  • 连接建立方式
    • SSE 使用 HTTP 协议:
      • 连接建立过程与普通的 HTTP 请求类似,客户端向服务器发送一个特定的 HTTP 请求,服务器保持连接打开并不断发送事件流。
      • 基于现有的 HTTP 基础设施,易于实现和部署。
    • WebSocket 使用独立的协议:
      • 客户端和服务器通过握手建立 WebSocket 连接,使用的是一种不同于 HTTP 的协议。
      • 需要服务器和客户端都支持 WebSocket 协议。
  • 数据格式
    • SSE 数据格式简单:
      • 事件流是一种文本格式的数据,由一系列以特定格式组织的事件组成。每个事件包含事件类型、数据和可选的标识符等信息。
      • 易于解析和处理。
    • WebSocket 数据格式灵活:
      • 可以传输文本数据和二进制数据,适用于各种类型的应用场景。
      • 需要根据具体的应用需求进行数据的封装和解封。
  • 性能和效率
    • SSE 相对轻量级:
      • 由于是单向通信,连接建立和数据传输相对简单,对服务器资源的消耗较少。
      • 适合大规模的广播式推送场景。
    • WebSocket 性能较高:
      • 双向通信可以减少不必要的 HTTP 请求,提高通信效率。
      • 适用于需要频繁交互的场景,但对服务器的性能要求相对较高。
  • 跨域支持
    • SSE 和 WebSocket 都支持跨域请求:但需要服务器在响应头中设置适当的跨域访问控制(CORS)参数。
    • SSE 的跨域设置相对简单:由于基于 HTTP 协议,与其他 HTTP 请求的跨域设置类似。
    • WebSocket 的跨域设置稍微复杂一些:需要在服务器端进行额外的配置,以确保跨域连接的安全性。

有哪些新兴的双端通信技术?

  • WebRTC(Web Real-Time Communication):这是一种支持网页浏览器进行实时语音、视频通话和数据共享的技术。它允许用户在不安装额外插件的情况下,直接通过浏览器进行点对点的通信。WebRTC 技术已经被广泛应用于视频会议、在线教育、远程协作等场景。
  • HTTP/2 服务器推送:HTTP/2 协议的一个特性,允许服务器在客户端请求之前主动推送资源到客户端。这可以减少客户端的请求次数,提高页面加载速度和实时通信的效率。不过,该技术的应用需要服务器和浏览器的支持。
  • QUIC(Quick UDP Internet Connections):这是一种基于 UDP 的传输协议,旨在提供更快、更可靠的网络连接。QUIC 具有 0-RTT 建立连接、双边用户态协议栈加速、连接迁移、多路复用等优势,在直播、视频、点播、下载等场景得到广泛应用。
  • MQTT(Message Queuing Telemetry Transport):是一种轻量级的消息队列遥测传输协议,主要用于物联网设备之间的通信。它具有资源消耗少、效率高的优势,支持发布 / 订阅模式,可实现双向通信。
  • CoAP(Constrained Application Protocol):是一种专用于受限设备的互联网应用协议,运行在 UDP 之上。CoAP 具有消息模型、请求 / 响应模型、双向通信、轻量级、支持可靠传输等特点,适用于物联网等资源受限的环境。
  • 基于 UDP 的双端通信:南网科技获得的发明专利 “基于 UDP 双端通信交互的数据传输方法、系统及设备”,实现了终端设备与平台之间采用 UDP 通信协议交互,无需建立连接。这种方式可以确保平台接收终端设备上传的报文数据的完整性,不会丢失数据。

安全

XSS

  • 原理 Cross Site Scripting(跨站脚本攻击)。XSS 是一种注入式攻击,攻击者通过在目标网站注入恶意脚本,当用户访问该网站时,恶意脚本在用户浏览器中执行,可能窃取用户的敏感信息、篡改页面内容等。
  • 危害
    • 盗取用户的 Cookie 信息:攻击者可以通过 XSS 注入恶意脚本代码,窃取用户的 Cookie 信息(通过document.cookie获取),然后利用这些信息进行各种非法操作,如盗取用户的账号、发送恶意邮件等。
    • 窃取用户的敏感信息:攻击者可以通过 XSS 注入恶意脚本代码,例如通过监听键盘输入时间,获取用户的敏感信息,如账户名、密码、银行卡号等。
    • 控制用户的浏览器:攻击者可以通过 XSS 注入恶意脚本代码,控制用户的浏览器,执行各种非法操作,如弹出广告、下载恶意软件等。
    • 攻击其他用户:攻击者可以通过 XSS 注入恶意脚本代码,攻击其他用户,如在论坛中发布恶意评论、窃取其他用户的 Cookie 信息等。
  • XSS 通常利用的漏洞
    • 缺乏输入验证:Web 应用程序未对用户输入的数据进行合法性验证或过滤,导致恶意脚本能够被成功注入到 Web 页面中。
    • 缺乏输出过滤:Web 应用程序未对输出到 Web 页面中的数据进行合适的过滤,导致恶意脚本能够被成功渲染和执行。
    • 跨站脚本漏洞:Web 应用程序在输出到Web页面中的数据中,未对敏感字符进行转义,导致攻击者能够在 Web 页面中注入恶意脚本。
    • 恶意广告:恶意广告是指攻击者在广告中嵌入恶意代码,以实现 DOM 型 XSS 攻击。用户在访问包含恶意广告的网站时,恶意代码会被执行,从而导致受害者的敏感信息泄露。
    • 恶意浏览器扩展:恶意浏览器扩展是指攻击者制作的、以浏览器扩展程序的形式存在的恶意代码,用于实现 DOM 型 XSS 攻击。

存储型/持久型(Server端)

场景:见于带有用户保存数据的网站功能,如论坛发帖、商品评论、用户私信等。

攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中。
  2. 用户打开目标网站时,服务端将恶意代码从数据库中取出来,拼接在HTML中返回给浏览器。
  3. 用户浏览器在收到响应后解析执行,混在其中的恶意代码也同时被执行。
  4. 恶意代码窃取用户数据,并发送到指定攻击者的网站,或者冒充用户行为,调用目标网站的接口,执行恶意操作。

反射型/非持久型(Server端)

场景:通过 URL 传递参数的功能,如网站搜索、跳转等。与存储型的区别在于,存储型的恶意代码存储在数据库中,反射型的恶意代码在 URL 上。

攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL 时,网站服务端将恶意代码从 URL 中取出,拼接在 HTML 中返回给浏览器。
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

DOM 型(浏览器端)

场景:通过 URL 传递参数的功能,如网站搜索、跳转等。DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JavaScript 自身的安全漏洞,而其他两种 XSS 都属于服务端的安全漏洞。

攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码。
  2. 用户打开带有恶意代码的 URL。
  3. 用户浏览器接收到响应后解析执行,前端 JavaScript 取出 URL 中的恶意代码并执行。
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户的行为,调用目标网站接口执行攻击者指定的操作。

XSS 防范措施

  • 使用 HTTPOnly Cookie 这是预防 XSS 攻击窃取⽤户 Cookie 最有效的防御⼿段。Web 应⽤程序在设置 Cookie 时,将其属性设为 HttpOnly,就可以防止恶意脚本通过document.cookie获取用户的 Cookie 信息。
    const http = require('http')
    
    http.createServer((req, res) => {
      res.writeHead(200, {
        'Set-Cookie': 'mycookie=value; HttpOnly'
      })
      res.end('Hello, world!')
    }).listen(3000)
    
  • 设置 CSP
    • 内容安全策略(Content Security Policy)是⼀个附加的安全层,⽤于帮助检测和缓解某些类型的攻击,包括 XSS 和数据注⼊等攻击。这些攻击可⽤于实现从数据窃取到⽹站破坏或作为恶意软件分发版本等⽤途。
    • CSP 本质上就是建⽴⽩名单,由浏览器进行拦截。开发者明确告诉浏览器哪些外部资源可以加载和执⾏。我们只需要配置规则,如何拦截是由浏览器⾃⼰实现的。我们可以通过这种⽅式来尽量减少 XSS 攻击。
    • Content-Security-Policy: default-src 'self':所有内容均来自站点的同一个源(不包括其子域名)
    • Content-Security-Policy: default-src 'self' *.trusted.com:允许内容来自信任的域名及其子域名(域名不必与 CSP 设置所在的域名相同)
    • Content-Security-Policy: default-src xxxx.com:该服务器仅允许通过 HTTPS 方式并仅从xxxx.com域名来访问文档
    <meta http-equiv="Content-Security-Policy" content="default-src 'self'">
    
  • 对用户的输入进行验证过滤 验证和过滤的目的是确保用户输入的数据符合预期的格式和内容,避免恶意脚本或非法字符的注入。验证和过滤一般使用正则表达式或其他相关函数对输入的数据进行处理。
  • 对输出到页面的内容进行转义 转义的目的是将输出到页面的数据进行转换,避免被浏览器误解为 HTML 代码,从而避免 XSS 攻击的发生。同时,转义只是一种防范 XSS 攻击的措施,还需要结合其他安全措施,例如输入验证、输出过滤、Cookie 安全标记等,来提高应用程序的安全性。在前端开发中,通常可以使用一些转义函数来进行转义处理,例如 JavaScript 中的 escape 和 encodeURIComponent 函数。
    • HTML 实体编码转义
      // 通常需要转义的字符 
      //  <、>、"、&、/
      function escapeHtml(str) { 
        return str.replace(/[&<>"']/g, (match) => {
          switch (match) { 
            case '&': return '&amp;'; 
            case '<': return '&lt;'; 
            case '>': return '&gt;'; 
            case '"': return '&quot;'; 
            case "'": return '&#39;'; 
          }
        })
      }
      
      • <>:这两个字符用于 HTML 标签的起始和结束,如果不进行转义,可能会被当作标签解析,导致安全问题。
      • "':这两个字符用于定义 HTML 属性的值,如果不进行转义,可能会被当作属性值的起始和结束,导致属性值不正确或安全问题。
      • &:这个字符用于 HTML 实体引用,如果不进行转义,可能会被当作实体引用的起始符号,导致 HTML 语法错误或安全问题。
      • /:这个字符用于路径分隔符,如果不进行转义,可能会被当作路径分隔符,导致路径解析错误或安全问题。
      • %:这个字符用于 URL 编码中,如果不进行转义,可能会导致URL解析错误或安全问题。
    • JavaScript 字符串转义 将 JavaScript 字符串中的特殊字符转换为对应的转义字符。例如,将反斜杠\转义为 \\,将换行符转义为\n,将制表符转义为\t
    • URL 编码转义 将 URL 中的特殊字符转换为对应的 URL 编码。例如,将空格转换为%20,将加号+转换为%2B,将问号?转换为%3F

CSRF

  • 原理 Cross Site Request Forgery(跨站请求伪造),攻击者通过伪造用户请求,使用户在不知情的情况下执行某些操作,例如发送邮件、转账等。
  • 攻击流程
    1. 受害者登录a.com,并保留了登录凭证(Cookie)
    2. 攻击者引诱受害者访问了b.com
    3. b.coma.com发送了一个请求:a.com/act=xx浏览器会默认携带a.com的 Cookie
    4. a.com接收到请求后,对请求进行验证,并确认是受害者的凭证,误以为是受害者自己发送的请求
    5. a.com以受害者的名义执行了act=xx
    6. 攻击完成,攻击者在受害者不知情的情况下,冒充受害者,让a.com执行了自己定义的操作
  • 攻击类型
    • ET 型:如在页面的某个 img 中发起一个 get 请求
    • POST 型:通过自动提交表单到恶意网站
    • 链接型:需要诱导用户点击链接
  • 攻击原理 从网络底层协议(主要是 HTTP 协议)的角度来看,CSRF 攻击能够成功主要是利用同源策略的漏洞:
    • 在使用 Ajax(XMLHttpRequest 或 Fetch API)进行跨域请求时,确实是默认不带 Cookie 的,除非明确设置了 withCredentials 属性。
    • 但在进行其他类型的跨站请求(如<img><form>等)时,浏览器会自动带上目标站点的 Cookie。这些都是所谓的简单请求,它们不会触发 CORS 预检请求,因此直接发出,并且会自动附带目标站点(目标站点和请求发起站点不同)的 Cookie。
  • 危害
    • 窃取用户账号信息和其他个人隐私信息
    • 伪造用户操作,例如转账、发邮件等
    • 修改用户信息,例如更改密码、修改收货地址等

CSRF 防范措施

  • 验证来源:在服务器端验证请求的来源是否是可信的,例如验证 Referer、Origin、CSRF Token等信息。(Referer 是 HTTP 协议中的一个请求头字段,全称为referring page,即引用页。它的作用是告知服务器当前请求的来源页面地址)
  • 使用 CSRF Token:在表单中添加一个随机生成的 Token,服务器端验证请求的时候需要验证该 Token 的合法性。
  • 限制访问:对于某些敏感的操作,限制访问来源,例如需要在特定的 IP 地址、特定的网络环境等条件下才能进行访问。
  • 禁用第三方 Cookie:避免攻击者利用第三方 Cookie 进行 CSRF 攻击。
  • 对用户操作进行二次确认:在执行某些敏感的操作之前,要求用户进行二次确认,例如要求用户输入密码、输入验证码等。

SQL 注入

  • 原理 SQL 注入是一种常见的攻击方式,攻击者通过在应用程序的输入框等地方输入恶意代码,以获取或篡改数据库中的数据。攻击者可以利用这种漏洞,绕过登录、获取敏感数据等。
  • 攻击原理 攻击者向应用程序提交带有恶意 SQL 代码的请求,如果应用程序没有对输入进行过滤或验证,那么恶意代码就可以被执行,从而访问或篡改数据库中的数据。
  • 危害 SQL 注入可以造成严重的安全问题,例如修改、删除、泄露数据库中的数据,以及拿到管理员账户和密码等。
  • 防范措施 避免拼接 SQL 语句,使用参数化的SQL语句,对用户输入的数据进行过滤和验证,设置良好的用户权限,定期进行安全漏洞扫描等。

中间人攻击

  • 原理 中间人攻击是攻击者通过劫持网络传输的数据包,获取敏感信息。例如,在用户登录网银时,攻击者可以通过劫持数据包的方式,获取用户的账号和密码等信息。
  • 攻击原理 攻击者可以在用户和服务器之间插入自己的代理服务器,将用户发送的请求转发到服务器,同时也能够获取服务器返回的响应数据。攻击者可以修改数据包中的内容,从而获取敏感信息。
  • 危害 中间人攻击可以获取用户的敏感信息,例如账户密码、银行卡号等,从而造成财产损失和个人信息泄露等安全问题。
  • 防范措施 使用HTTPS协议进行数据传输,通过数字证书来验证服务器的身份,从而确保与服务器之间的通信是加密的且没有被篡改的。

iframe 劫持

  • 原理 这是一种视觉欺骗的攻击手段。攻击者将一个透明的、不可见的 iframe 覆盖在合法的网页上,诱使用户在不知情的情况下点击 iframe 中的恶意链接或执行其他恶意操作。
  • 防范措施 使用X-Frame-Options响应头来指示浏览器是否允许在 iframe 中加载页面。可以设置为 DENY(完全不允许在 iframe 中加载)、SAMEORIGIN(只允许在同源的 iframe 中加载)或 ALLOW-FROM <uri>(只允许从特定的 uri 加载)。

HTTP 的流加载

  • HTTP 流加载原理:HTTP 流加载是一种在 HTTP 协议基础上,让客户端(如浏览器)能够以流(stream)的形式接收服务器端发送的数据的方式。传统的 HTTP 请求通常是客户端发送请求,等待服务器响应整个数据内容,然后客户端一次性接收完成后再进行处理。而流加载则不同,服务器可以持续地发送数据,客户端可以在数据还未完全接收时就开始处理已经收到的部分数据。
  • 应用场景示例:在视频播放场景中,当用户请求播放一个在线视频时,服务器可以通过 HTTP 流加载技术,将视频数据以流的形式发送给客户端。客户端(如浏览器的视频播放器)可以一边接收数据,一边解码和播放视频,而不需要等待整个视频文件下载完成。这样可以大大减少用户的等待时间,提高用户体验。
  • 客户端实现方式
    • 使用 Fetch API 实现 HTTP 流加载
      • 基本原理:Fetch API 是现代浏览器中用于获取资源的接口。当设置 responseType 为 stream 时,它允许以流的形式获取服务器响应的数据。这样,数据会以小块(chunk)的形式逐步接收,而不是等待整个响应完成后一次性接收。
      • 示例代码及步骤
        • 发起流加载请求:
          const url = 'http://example.com/streaming-data';
          const controller = new AbortController();
          const signal = controller.signal;
          const response = await fetch(url, {
            signal,
            responseType:'stream'
          });
          
          fetch 函数用于发起请求。responseType 被设置为 stream,表示期望以流的形式接收响应。AbortController 和 signal 用于在需要时取消请求。
        • 读取流数据:
          const reader = response.body.getReader();
          const processData = async () => {
            while (true) {
              const { done, value } = await reader.read();
              if (done) {
                break;
              }
              // 处理接收到的数据块
              const data = new TextDecoder().decode(value);
              console.log(data);
            }
          };
          processData();
          
          首先,通过response.body.getReader()获取一个 Reader 对象,用于读取流中的数据块。在 processData 函数中,reader.read()方法会返回一个包含 done 和 value 的对象。done 表示是否已经读取完所有数据,value 是一个包含数据块的 Uint8Array 。通过TextDecoder().decode(value)可以将数据块解码为文本格式,然后进行处理(这里只是简单地打印到控制台)。
    • 使用 XMLHttpRequest 实现 HTTP 流加载
      • 基本原理:XMLHttpRequest 是一个较旧但仍然广泛支持的用于浏览器与服务器通信的接口。通过设置 responseType 为 ms-stream(在 IE 浏览器)或 text(其他浏览器,结合手动处理数据块),并监听 progress 事件来实现流加载。在 progress 事件中,可以获取已经接收到的数据部分并进行处理。
      • 示例代码及步骤
        • 发起流加载请求:
          const xhr = new XMLHttpRequest();
          xhr.open('GET', 'http://example.com/streaming-data', true);
          xhr.responseType = 'text';
          xhr.onprogress = function (e) {
            if (e.lengthComputable) {
              const dataChunk = e.target.responseText.substring(e.loaded - e.lengthComputable, e.loaded);
              // 处理数据块
              console.log(dataChunk);
            }
          };
          xhr.onload = function () {
            if (xhr.status === 200) {
              // 处理剩余数据(如果有)
              console.log(xhr.responseText);
            }
          };
          xhr.send();
          
          首先创建 XMLHttpRequest 对象,设置请求方法为 GET,请求 URL,并将 responseType 设置为 text。onprogress 事件监听器用于在数据接收过程中处理数据块。通过e.target.responseText.substring获取当前接收到的数据块。onload 事件监听器用于在请求完成后处理剩余的数据(如果有)。
    • 使用第三方库(如 RxJS)实现 HTTP 流加载
      • 基本原理:RxJS 是一个用于处理异步事件流的库。它可以将 HTTP 流数据转换为可观察对象(Observable),然后利用 RxJS 的各种操作符来处理流数据,如过滤、映射、合并等操作。
      • 示例代码及步骤
        • 发起流加载请求并转换为 Observable:
          import { from } from 'rxjs';
          import { map } from 'rxjs/operators';
          const url = 'http://example.com/streaming-data';
          const controller = new AbortController();
          const signal = controller.signal;
          const response = await fetch(url, {
            signal,
            responseType:'stream'
          });
          const reader = response.body.getReader();
          const streamObservable = from(new ReadableStream({
            async start(controller) {
              while (true) {
                const { done, value } = await reader.read();
                if (done) {
                  break;
                }
                controller.enqueue(value);
              }
              controller.close();
            }
          }));
          
          首先,使用 fetch 发起流加载请求并获取 Reader 对象,然后通过 from 函数将自定义的 ReadableStream 转换为 RxJS 的 Observable。在 start 函数中,不断读取 ReadableStream 的数据块并将其放入 Observable 的队列中。
        • 使用 RxJS 操作符处理流数据:
          streamObservable.pipe(
            map((value) => new TextDecoder().decode(value))
          ).subscribe((data) => {
            console.log(data);
          });
          
          通过 pipe 操作符和 map 函数将 Observable 中的数据块解码为文本格式,然后通过 subscribe 订阅这个 Observable,在数据到达时进行处理(这里是打印到控制台)。这种方式可以方便地结合 RxJS 的其他操作符来实现更复杂的流数据处理。