HTTP和HTTPS的区别
HTTP通常承载于TCP 之上,在HTTP 和 TCP 之间添加一个安全协议层 (SSL 或 TLS), 这个时候,就成了我们常说的HTTPS。
使用 HTTPS 可能看到: “283hd9saj9cdsncihquhs99ndso”。HTTPS 传输的不再是文本,而是二进制流,使得传输更高效,且加密处理更加安全。
-
HTTP 标准
端口是80 ,而 HTTPS 的标准端口是443 -
HTTP 是不
安全的,而 HTTPS 是安全的:HTTP 无法加密,而HTTPS 对传输的数据进行加密,HTTP无需证书,而HTTPS 需要CA机构证书,功能越强大的证书费用越高 -
HTTPS由于需要设计加密以及多次握手(TLS握手),性能方面不如HTTP
-
HTTP是直接与TCP进行数据传输,而HTTPS运行在SSL/TLS(安全传输层协议)之上 -
SEO搜索引擎优先抓取 HTTPS 页面
与HTTPS相比,HTTP有什么缺点?
- 通信使用明文,内容可能被窃听,也就是被抓包分析。
- 不验证通信方身份,可能遭到伪装。
- 无法验证报文完整性,可能被篡改。
HTTPS就是HTTP+加密处理(一般是SSL安全通信线路)+认证+完整性保护。
为什么HTTPS更安全 ⭐️⭐️⭐️
之所以比HTTP更安全,是因为它在HTTP的基础上增加了加密传输、身份验证和数据完整性验证三大核心机制
SSL 的实现这些功能主要依赖于三种手段:
- 对称加密(如 AES):加密/解密使用同一密钥,效率高。
- 非对称加密(如 RSA):公钥加密、私钥解密,用于安全交换对称密钥。
- 混合加密:TLS 握手阶段用非对称加密交换密钥,后续通信用对称加密。
- 摘要算法:数据完整性校验
- 数字证书:身份验证
1. 加密传输:防止窃听
- HTTP的缺陷:HTTP传输的数据是明文(未加密),
黑客可以通过网络嗅探工具(如Wireshark)直接截获并查看内容(如密码、信用卡号等)。 - HTTPS的加密:通过SSL/TLS协议对数据进行加密,传输内容变为密文,即使被截获也无法直接破解。
2. 数据完整性:防止篡改
- HTTP的缺陷:明文数据在传输中可能被篡改(
如插入广告或恶意代码)。 - HTTPS的保护:使用消息认证码(MAC)或哈希算法(如SHA-256)对数据生成 “指纹”。
3. 身份认证:防止伪装(中间人攻击)
- HTTP的风险:攻击者可伪造服务器身份(如假冒银行网站),诱导用户提交敏感信息,避免中间人攻击(如钓鱼网站)。。
- HTTPS的解决方案:服务器必须提供由受信任的证书颁发机构(CA)签发的数字证书,证明其身份。
实际应用中的安全机制
- HSTS(HTTP严格传输安全):强制浏览器仅通过HTTPS访问网站,防止协议降级攻击。
- SNI(服务器名称指示):支持同一IP地址托管多个HTTPS网站,避免证书不匹配。
- 前向保密(Forward Secrecy):每次会话使用临时密钥,即使长期私钥泄露,历史通信仍安全。
- 使用 HTTPS 后访问变慢:TLS 握手耗时,可通过缓存连接、启用 HTTP/2、优化证书链来缓解
总结
HTTPS通过加密传输、身份认证和数据完整性验证,解决了HTTP的三大安全隐患,是互联网安全的基石。现代网站(尤其是涉及隐私的)必须启用HTTPS,否则会被浏览器标记为“不安全”,并影响SEO排名。
http不安全,为什么仍有网站使用http?
- 成本高
- 技术门槛高
- 兼容老设备
- 静态信息不需要不需要https
HTTP协议有哪些特征?
- 简单快速:客户向服务器请求服务时,只需传送请求方法和路径。由于 HTTP 协议简单,使得 HTTP 服务器的程序规模小,因而通信速度很快
- 灵活:HTTP 允许传输任意类型的数据对象。正在传输的类型由 Content-Type 加以标记
- 无连接:无连接的含义是限制每次连接只处理一个请求。服务器处理完客户的请求,并收到客户的应答后,即断开连接。采用这种方式可以节省传输时间
- 无状态:HTTP 协议无法根据之前的状态进行本次的请求处理
HTTP1.1版本的新特性有哪些 ⭐️⭐️5点?
-
持久连接:HTTP1.0 中,每次请求 - 响应完成后,连接就会关闭,而 HTTP1.1 支持持久连接,只要客户端/服务端中任意一端没有明确指出断开TCP连接,就一直保持连接。减少了建立和关闭连接的开销,提高了传输效率
-
管线化:在一次 TCP 连接中可以传输多个 HTTP 请求和响应,而不用等待前一个请求的响应回来再发下一个,提高了传输效率 。
-
分块传输编码:当服务器不知道响应数据的长度时,可使用分块传输编码,将数据分成多个块进行传输,增强了传输的灵活性。
-
引入
Cache-Control缓存:能更精准地控制页面缓存,提高缓存的有效性和安全性。 -
Host 头字段:支持在同一个
IP 地址和端口号上部署多个网站,通过Host头字段来区分不同的站点
说出你知道的HTTP常见状态码 ⭐️⭐️(重点)
以下是面试常问的重点状态码及其含义:
200 OK:请求成功(最常见)201 Created:资源创建成功(如 POST 新建)204 No Content:请求成功但无返回内容(如 DELETE)301/302:重定向,前者为永久,后者为临时(SEO 优化相关)304 Not Modified:缓存优化场景(结合 ETag / Last-Modified)400 Bad Request:请求语法错误,如参数不合法401 Unauthorized:未认证,通常要求登录或提供 token403 Forbidden:已认证但无权限访问404 Not Found:资源不存在,常用于错误页面429 Too Many Requests:请求频率过高,通常用于限流策略500 Internal Server Error:服务端异常,前端应展示友好提示502 Bad Gateway/503 Service Unavailable:网关错误、服务不可用,通常和反向代理或负载均衡相关
-
100 Continue 继续请求,表明客户端应当继续发送请求,服务器已收到请求头,若客户端继续发送请求主体,服务器将处理。
一般在发送post请求时,已发送了HTTP header 之后,服务器端将返回此信息,表示确认,之后发送具体参数信息。 -
101 切换协议 客户端要求服务器切换协议,服务器已确认并准备切换。
在实际应用场景中,当客户端发起升级协议的请求(如从 HTTP 协议升级到 WebSocket 协议),且服务器同意进行协议切换时,就会返回 101 状态码。比如,在网页实时聊天、在线游戏等需要双向通信的场景中,常通过这种方式从 HTTP 协议升级到 WebSocket 协议,建立更高效的长连接,实现实时数据传输。 -
200 OK 请求成功,正常返回信息,服务器已经成功处理了请求。通常,这表示服务器提供了请求的网页
-
201 Created 表示请求成功并且服务器创建了新的资源。
在实际应用中,当用户通过 POST 或 PUT 请求向服务器提交数据,成功创建新的资源(如在数据库中插入新记录、创建新文件等)时,服务器通常会返回 201 状态码。同时,响应头会包含一个 “Location” 字段,该字段的值是新创建资源的 URL,方便客户端进一步访问。例如,在向一个博客平台发布新文章后,服务器返回 201 状态码,“Location” 字段可能指向该文章的具体页面链接 -
202 Accepted 表示服务器已接受请求,但尚未完成处理(处理中)。
这一状态码常见于异步操作场景,比如提交一个复杂的任务请求时,服务器接收到请求并确认可以处理,但任务处理需要一定时间,此时就返回 202 状态码。像在一些云存储服务中上传大文件,用户发起上传请求后,服务器接受了上传任务,但由于文件较大需要时间处理,就会先返回 202 状态码,让用户知道请求已被认可,之后再通过其他方式通知用户任务完成情况。 -
204 No Content 该状态码代表服务器接收的请求已成功处理,但在返回的响应报文中不含实体的主体部分。另外,也不允许返回任何实体的主体。
常见应用场景是在执行某些操作后,客户端只需要知道操作是否成功,不需要服务器返回具体数据。例如,使用 DELETE 方法删除资源,若删除成功,服务器可能返回 204 状态码,告诉客户端资源已成功删除,且无需额外数据响应。还有执行更新操作时,如果客户端仅关注更新是否完成,服务器也会返回 204,减少不必要的数据传输 -
206 Partial Content 该状态码表示客户端进行了范围请求,而服务器成功执行了这部分的GET请求。
常见的应用场景是在进行文件下载时,若网络中断后恢复下载,客户端可通过请求头中的 Range 字段指定需要下载的文件片段,服务器收到请求后返回 206 状态码,以及客户端指定范围的文件内容,实现断点续传功能。此外,在处理大文件或大数据集时,也可使用 206 状态码按范围获取数据,减少一次性传输的数据量,提高传输效率和响应速度 -
301 Moved Permanently 表示永久重定向,请求的网页已永久移动到新位置。
搜索引擎优化(SEO)方面:当网站的某个页面 URL 发生永久性变更时,为了避免影响搜索引擎的收录和用户访问,网站管理员会设置 301 重定向。比如,旧版网站页面www.example.com/old - page被调整为www.example.com/new - page,就设置 301 重定向,搜索引擎会将旧页面的权重转移到新页面,用户访问旧链接时也会自动跳转到新页面。 -
302 Found 表示临时性重定向。
在实际应用中,当网站举办限时活动,活动页面原本的 URL 为www.example.com/event,但临时调整到www.example.com/temp - event时,服务器会返回 302 状态码,同时在响应头的Location字段中指明临时页面的地址。这样,用户访问原 URL 时会被临时引导至新地址。不过活动结束后,原 URL www.example.com/event仍能恢复使用,而不像 301 重定向那样,原 URL 被永久性替代 -
303 See Other 表示临时性重定向(查看其他位置 ),它告诉客户端应通过另一个 URL 来获取资源,且建议使用 GET 方法获取,常见场景是在完成某个处理请求后,服务器希望客户端访问另一个页面
。例如,用户在网站上提交表单进行注册,注册成功后,服务器返回 303 状态码,并在Location字段指定一个注册成功的提示页面,如www.example.com/register - success。客户端收到 303 响应后,会使用 GET 方法去访问这个指定的 URL,从而看到注册成功的提示信息。 -
307 Temporary Redirect 临时重定向。它和 302 类似,都表明请求的资源临时移动到了另一个 URL,但 307 更为严格。307 要求客户端在重定向时,保持原请求的方法(如 GET、POST 等)不变。
在实际应用中,比如一个电商网站在进行系统升级时,把商品详情页临时迁移到新的服务器地址。当用户请求原商品详情页链接时,服务器返回 307 状态码,在Location字段中给出新的商品详情页地址。若用户之前是通过 POST 请求访问原页面(例如携带特定筛选条件查看商品详情),那么在重定向到新地址时,依然会使用 POST 请求,保证请求信息不丢失,用户仍能看到符合筛选条件的商品详情 -
304 Not Modified (未修改)表示自从上次请求后,请求的资源未修改过,客户端可以继续使用缓存的资源。
-
400 Bad Request 错误请求,它意味着客户端发送的请求存在语法错误或无法被服务器理解。
常见原因及场景有:请求参数缺失或错误,在向服务器提交表单时,如果必填项未填写,或者填写的参数格式不符合服务器预期,服务器就会返回 400 状态码;比如在注册账号时,用户名或密码为空,或者密码长度不符合规定。另外,请求头信息错误也会出现这种情况,若客户端发送的请求头中包含不支持或错误的字段,服务器无法处理,也会返回 400。 -
401 Unauthorized(请求未授权,需要身份验证)对它表明客户端试图访问受保护的资源,但未提供有效的身份验证凭证,或者提供的凭证无效。
用户访问一个需要登录的网站,但没有输入用户名和密码或者输入的登录信息有误,服务器就会返回 401 状态码,提示用户需要进行身份验证才能访问相应资源。例如,在访问个人电子邮箱时,若未登录账号就尝试查看邮件,邮箱服务器会返回 401,提示用户进行登录授权 。 -
403 Forbidden (禁止访问)服务器拒绝请求。它意味着服务器理解客户端的请求,但拒绝执行该请求,即客户端没有权限访问请求的资源。权限不足:
这是最常见的原因。比如在一个企业内部管理系统中,普通员工试图访问只有管理员才能查看的财务报表页面,即使员工已登录系统完成身份验证,服务器仍会返回 403 状态码,因为普通员工没有对应的访问权限。服务器配置限制:服务器可能基于自身安全策略或配置,禁止特定 IP 地址、用户代理等访问资源。例如,网站为防范恶意爬虫,会在服务器上设置禁止某些 IP 段的访问,当处于这些 IP 段的用户访问网站时,就会收到 403 错误。资源不可用或受保护:像网站上的某些测试页面、正在维护的功能模块,为避免外部用户误访问,会设置禁止访问,此时用户尝试访问就会得到 403 状态码。 -
404 Not Found 表示找不到如何与URI相匹配的资源。
-
500 Internal Server Error(内部服务器错误)。它意味着服务器在处理请求时遇到了意外情况,导致无法完成请求。也有可能是 Web 应用存在的 bug 或某些临时的故障。代码错误:
服务器端的应用程序代码存在缺陷,如语法错误、逻辑错误、空指针异常等。服务器配置问题:服务器的配置参数设置不当,如内存分配不足、数据库连接配置错误等。比如,网站访问量突然增加,而服务器分配的内存无法满足处理大量请求的需求,就可能导致服务器内部错误,返回 500 状态码。 -
502 Bad Gateway(网关错误) 它通常发生在服务器作为网关或代理服务器,从上游服务器(如 Web 服务器或其他代理服务器)收到无效响应时。上游服务器故障:
当网站使用了负载均衡器或反向代理服务器,若后端的某台 Web 服务器出现故障,如服务器崩溃、进程意外终止等,负载均衡器将请求转发到该故障服务器后,无法获得有效的响应,此时负载均衡器就会向客户端返回 502 状态码。例如,一个电商平台有多台服务器处理用户请求,其中一台服务器因硬件问题突然宕机,用户请求被转发到这台服务器时就会收到 502 错误。网络连接问题:在代理服务器与上游服务器之间的网络连接不稳定或中断时,代理服务器无法将请求成功发送给上游服务器,或者无法接收上游服务器的响应,从而导致 502 错误。比如网络出现拥塞、线路故障等情况,影响了数据传输,使得代理服务器获取不到有效响应。上游服务器过载:当上游服务器的处理能力达到极限,无法及时处理大量的请求时,可能会拒绝连接或返回无效响应。例如,在一些热门活动期间,大量用户同时访问网站,后端服务器负载过高,无法正常处理请求,就可能导致代理服务器收到无效响应并返回 502 状态码给客户端。 -
504 Gateway Timeout (网关超时) 服务器作为网关或代理,但是没有及时从上游服务器收到请求。
-
503 Service Unavailable (服务不可用) 表示服务器端暂时无法处理请求。通常是由于服务器过载、正在进行维护或者停机等原因造成的
状态码401和403的区别
简单来说,401 侧重于身份未被验证,通常是登录相关的问题;
而 403 侧重于已验证身份,但权限不足,无法访问特定资源。
HTTP 状态码 401 和 403 都表示客户端请求访问资源时出现了权限相关的问题,但它们之间有一些细微的区别:
-
401 Unauthorized(未授权)
- 含义:对它表明客户端试图访问受保护的资源,但未提供有效的身份验证凭证,或者提供的凭证无效。。
- 示例场景:用户访问一个需要登录的网站,但没有输入用户名和密码或者输入的登录信息有误,服务器就会返回 401 状态码,提示用户需要进行身份验证才能访问相应资源。例如,在访问个人电子邮箱时,若未登录账号就尝试查看邮件,邮箱服务器会返回 401,提示用户进行登录授权 。
-
403 Forbidden(禁止访问)
- 含义:它意味着服务器理解客户端的请求,但拒绝执行该请求,即客户端没有权限访问请求的资源。
- 示例场景:一个普通用户尝试访问管理员权限才能访问的系统设置页面,服务器在验证用户身份后,发现用户没有管理员权限,就会返回 403 状态码,禁止用户访问该页面。
状态码502和504的区别
502 强调的是收到的响应无效,它通常发生在服务器作为网关或代理服务器,从上游服务器(如 Web 服务器或其他代理服务器)收到无效响应时。通常是由于
上游服务器的问题导致无法正常处理请求;504 强调的是请求等待时间过长,没有及时从上游服务器收到请求,主要是由于响应延迟造成的。
502 Bad Gateway(网关错误)
- 含义:此状态码表示服务器作为网关或代理,从上游服务器收到了无效的响应。通常是由于服务器与其他服务器之间的通信出现问题,导致无法正确转发请求或获取响应。
- 常见原因:可能是上游服务器暂时不可用、崩溃、配置错误,或者是网关服务器与上游服务器之间的网络连接出现故障等。例如,微服务架构中,某个服务节点出现故障,导致请求该服务的网关返回 502 错误。
504 Gateway Time - out(网关超时)
- 含义:该状态码表明服务器作为网关或代理,在等待上游服务器响应请求时超过了预定的时间。这意味着请求在服务器之间的传递过程中花费了太长时间,最终导致超时。
- 常见原因:可能是上游服务器负载过高,处理请求的速度过慢,或者是网络延迟过大,导致请求无法在规定时间内完成。比如,服务器需要调用多个外部接口获取数据,但其中某个接口响应特别慢,就可能引发 504 错误。
http 状态码中 301,302和307有什么区别 ⭐️
重定向307、303和302之间的主要区别体现在以下方面:
307 和 302 类似,都表明请求的资源临时移动到了另一个 URL,但 307 更为严格。307 要求客户端在重定向时,保持原请求的方法(如 GET、POST 等)不变。
303 See Other 表示临时性重定向(查看其他位置 ),它告诉客户端应通过另一个 URL 来获取资源,且建议使用 GET 方法获取,常见场景是在完成某个处理请求后,服务器希望客户端访问另一个页面
浏览器兼容性:由于部分浏览器不能识别307响应,因此响应中需要包含必要的信息,如指向新的URI的超链接及简短说明,以便用户能够理解并向新的URI发出访问请求。而302和303则没有这方面的特殊要求。
协议版本:302和301是HTTP 1.0中的内容,而303、307等则是HTTP 1.1中引入的。
302 与 JS location.href 的区别?
浏览器收到服务器返回的 302 状态码时,会按照固定的自动化流程处理,无需用户和前端代码介入,核心流程如下:
1. 解析响应,提取跳转目标
浏览器首先解析服务器的 HTTP 响应,重点读取两个核心内容:
- 响应状态码:确认是 302(
302 Found,临时重定向); - 响应头
Location字段:提取服务器指定的跳转目标 URL(这是浏览器跳转的唯一依据,若缺失Location字段,浏览器会忽略 302,不进行跳转,仅返回响应内容)。
2. 自动发起新的 HTTP 请求(核心步骤)
浏览器会自动、透明地向 Location 字段指定的目标 URL 发起一个新的 HTTP 请求:
- 这个过程对用户和前端 JS 代码是 “无感” 的,用户不会看到中间的 302 响应,只会最终看到跳转后的页面;
- 新请求的请求方法:默认情况下,针对 302 响应,浏览器会将原请求的 GET 方法保留,发起 GET 类型的新请求;若原请求是 POST 方法,部分浏览器会遵循「POST → GET」的转换(这也是 302 用于 POST 请求重定向时的一个注意点);
- 注意:对于 AJAX/axios/fetch 发起的异步接口请求,302 跳转有特殊限制:浏览器会自动完成「302 对应的新请求」,但这个新请求的响应结果只会返回给 AJAX 引擎,不会触发浏览器的页面跳转(即页面不会刷新,也不会跳转到目标 URL),这也是很多开发者疑惑 “接口返回 302 但页面没跳转” 的原因。
3. 处理新请求的响应结果
浏览器接收新请求的响应(来自 Location 目标 URL),并按照正常流程处理:
- 若新响应是 200 状态码(正常页面),则渲染该页面,替换当前浏览器窗口的历史记录(302 对应的历史记录会被新页面覆盖,点击浏览器 “返回” 按钮会回到跳转前的上一页);
- 若新响应仍是 302/301 等重定向状态码,则重复上述流程,直到获取到非重定向响应或重定向次数超过浏览器限制(通常默认 5 次左右,超过则报错 “重定向次数过多”)。
补充特殊场景说明
- 跨域 302:若
Location目标 URL 是跨域地址,浏览器会正常发起新请求,但会遵循跨域资源共享(CORS)规则,若目标服务器未配置允许跨域,新请求会失败,且不会触发页面跳转; - 302 与 Cookie:跳转过程中,浏览器会自动携带目标域名对应的合法 Cookie,这也是部分登录重定向能保持会话的核心原因。
302 与 JS location.href 的核心区别
-
302 是服务器端发起的 HTTP 重定向,
location.href是客户端 JS 触发的页面跳转,核心区别在「发起方」和「执行层面」; -
302 适用于需要服务器控制跳转的场景,
location.href适用于前端自主控制跳转的场景。
在接口返回 302 时,浏览器是如何处理的?
304是什么 ⭐️⭐️⭐️ (重点)
HTTP 状态码 304 表示 “Not Modified(未修改)”,属于 3xx 重定向状态码。它主要用于缓存机制,告知客户端请求的资源未被修改,客户端可以直接使用本地缓存的副本,从而减少数据传输,提高访问速度和性能。
工作原理
- 当客户端首次请求一个资源时,服务器会返回该资源以及相应的缓存头信息,包括 Last - Modified(资源最后修改时间)或 ETag(实体标签,用于唯一标识资源的内容)等字段。
- 客户端再次请求相同资源时,会在请求头中带上 If - Modified - Since(对应 Last - Modified)或 If - None - Match(对应 ETag)字段,告知服务器本地缓存的资源信息。
- 服务器收到请求后,会根据客户端发送的信息与服务器上实际资源的状态进行对比。如果资源未被修改,服务器就会返回 304 状态码,同时不会在响应中包含资源的内容,客户端则使用本地缓存的资源。
优点
- 节省带宽:避免了重复传输相同的资源数据,特别是对于一些较大的文件或经常被访问的静态资源(如图片、脚本、样式表等),可以显著减少网络流量,降低服务器带宽压力。
- 提高性能:由于不需要再次从服务器获取资源,客户端可以直接从本地缓存中加载,大大缩短了资源的加载时间,提高了网页的加载速度,改善了用户体验。
应用场景
- 静态资源缓存:网站中的静态资源,如 HTML 页面、CSS 样式表、JavaScript 脚本、图片等,通常具有相对稳定的内容,适合使用 304 状态码进行缓存。只要资源没有被修改,客户端就可以一直使用本地缓存,减少与服务器的交互。
- 条件请求:在一些需要根据资源的更新状态来决定是否获取最新内容的场景中,如浏览器自动检查网页更新、应用程序检查软件更新等,会发送带有条件请求头的请求,服务器根据资源的实际情况返回 304 或最新的资源,以实现高效的更新检查机制。
收到 429 应如何处理?
收到 429 Too Many Requests 错误,核心处理原则是:识别限流类型 → 实现重试策略 → 优化请求频率。以下是精简的处理步骤,适配前端开发场景:
-
先看响应头,明确限流规则优先读取响应头的
Retry-After(告知多久后可重试,单位秒)、X-RateLimit-Limit(总限额)、X-RateLimit-Remaining(剩余次数),这是最合规的处理依据。 -
前端侧:安全重试 + 频率控制
-
重试策略:指数退避重试(第一次等 1s,第二次等 2s,第四次等 4s… 最多重试 3 次,避免死循环),禁止立即循环重试。
-
频率控制:
- 批量请求:加请求队列 + 节流 / 防抖,比如用
p-limit限制并发数,或按时间分片发送。 - 重复请求:缓存相同请求的结果(比如 GET 请求),避免重复触发。
- 非必要请求:延迟发送(如页面加载完成后)或按需加载。
- 批量请求:加请求队列 + 节流 / 防抖,比如用
-
-
后端侧(若你能控制服务)
- 配置合理的限流阈值,给前端返回清晰的
Retry-After头。 - 区分用户维度限流(如按 token、IP),避免全局限流影响所有用户。
- 配置合理的限流阈值,给前端返回清晰的
-
特殊场景:第三方 API
- 若调用第三方接口触发 429,优先查看其文档的限流规则,按文档要求调整。
- 必要时申请提高配额,或切换为批量接口(减少请求次数)。
前端代码极简示例(Vue2 + Axios)
// 指数退避重试的Axios拦截器
axios.interceptors.response.use(
(res) => res,
async (err) => {
const { config, response } = err;
// 仅处理429,且未超过最大重试次数
if (response?.status === 429 && !config.__retryCount) {
config.__retryCount = config.__retryCount || 0;
if (config.__retryCount >= 3) return Promise.reject(err);
// 计算退避时间:2^retryCount 秒
const delay = Math.pow(2, config.__retryCount) * 1000;
config.__retryCount++;
// 若有Retry-After,优先用它
const retryAfter = response.headers['retry-after'];
return new Promise(resolve => setTimeout(() => resolve(axios(config)), retryAfter ? retryAfter * 1000 : delay));
}
return Promise.reject(err);
}
);
核心总结429 的本质是请求频率超过服务端限制,前端处理的关键是 “不硬刚”—— 要么等,要么减少请求数,要么缓存结果。重试时必须加退避策略,避免加重服务端负担。
如果前端 fetch 接口请求返回了 204,应该怎么处理?
-
204 是「成功无数据」响应,无需当作异常处理;
-
fetch 处理核心避坑:不调用
response.json(),优先用response.text()或状态判断; -
业务侧重点:给用户清晰反馈,同步更新页面相关视图;
-
通用方案:先判断响应状态和
response.ok,再选择对应的解析 / 处理逻辑,提高兼容性。
常见Http请求头(重点)
HTTP 请求头是客户端向服务器发送请求时包含的附加信息,用于告知服务器有关请求的各种细节和客户端的相关信息。以下是一些常见的 HTTP 请求头:
-
Accept
- 作用:告诉服务器客户端能够接受的响应内容类型,例如
Accept: text/html表示客户端希望接收 HTML 格式的响应,Accept: application/json表示客户端期望接收 JSON 格式的数据。 - 示例:
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8,这里列出了多种可接受的内容类型,并通过q值(质量因子)表示优先级,q值越大优先级越高,省略q值时默认为 1,*/*表示接受任何类型。
- 作用:告诉服务器客户端能够接受的响应内容类型,例如
-
Accept - Encoding
- 作用:告知服务器客户端支持的内容编码方式,如
gzip、deflate等压缩算法。服务器可以根据此信息对响应内容进行压缩,以减少传输数据量。 - 示例:
Accept - Encoding: gzip, deflate, br,表示客户端支持这三种编码方式,服务器可选择其中一种进行压缩。
- 作用:告知服务器客户端支持的内容编码方式,如
-
Accept - Language
- 作用:用于指定客户端偏好的语言。服务器可以根据这个请求头,返回对应语言的内容。
- 示例:
Accept - Language: en - US,en;q=0.9,zh - CN;q=0.8,zh;q=0.7,表明客户端优先选择美式英语,其次是英语,然后是简体中文,最后还可以接受中文(可能包括各种中文变体),优先级为 0.7。
-
Cache - Control
- 作用:用于指定缓存相关的指令,如是否允许缓存、缓存的时长等,它可以覆盖浏览器默认的缓存策略。
- 示例:
Cache - Control: no - cache表示不使用缓存,每次都从服务器获取最新资源;Cache - Control: max - age=3600表示缓存有效期为 3600 秒。
-
Connection
- 作用:用于控制与服务器的连接方式,如是否保持连接以实现多个请求复用同一个连接。
- 示例:
Connection: keep - alive表示保持连接,使后续请求可以在同一个 TCP 连接上发送,提高性能;Connection: close则表示请求完成后关闭连接。
-
Cookie
- 作用:客户端向服务器发送存储在本地的 Cookie 信息,用于身份识别、会话管理等。服务器可以根据 Cookie 中的数据来识别用户身份、获取用户相关信息或维护用户的会话状态。
- 示例:
Cookie: sessionId=123456; username=JohnDoe,这里包含了名为sessionId和username的两个 Cookie。
-
Host
- 作用:指定请求的
**域名(或 IP 地址)和端口号(如果有),用于在一台服务器上托管多个网站或服务时,区分不同的虚拟主机。 - 示例:
Host: www.example.com或Host: www.example.com:8080,如果不指定端口号,默认使用 HTTP 的标准端口 80 或 HTTPS 的标准端口 443。
- 作用:指定请求的
-
Origin
- 作用:用于指示请求的来源站点,主要用于跨域请求中,服务器可以根据
Origin头来判断是否允许该跨域请求。 - 示例:
Origin: https://www.example.com,表示请求来自该 URL 对应的站点。
- 作用:用于指示请求的来源站点,主要用于跨域请求中,服务器可以根据
-
Referer
- 作用:标识了当前请求是从哪个页面发起的,服务器可以通过该字段了解用户的访问路径和来源,有助于统计分析和安全验证等。
- 示例:
Referer: https://www.example.com/page1.html,说明请求是从该页面发起的。
-
User - Agent
- 作用:客户端的相关信息,如浏览器类型、版本、操作系统等,服务器可以根据这些信息为不同的客户端提供适配的内容或进行统计分析。
- 示例:
User - Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36,通过这个字段可以看出用户使用的是 Windows 10 操作系统,Chrome 浏览器版本为 91.0 等信息。
-
Authorization
- 作用:告诉服务器客户端的相关信息,主要用于身份验证。客户端通过这个字段向服务器提供认证信息,证明自己有权访问受保护的资源 。当服务器设置了资源访问权限,要求客户端提供身份凭证时,客户端就会在请求中带上
Authorization头。 - 原理:将用户名和密码以
base64编码的方式组合在请求头中发送给服务器。格式为Authorization: Basic <base64编码后的字符串>,其中<base64编码后的字符串>是由用户名:密码经过base64编码得到的。 - 示例:假设用户名是
user,密码是password,组合成user:password,经base64编码后得到dXNlcjpwYXNzd29yZA==,那么请求头就是Authorization: Basic dXNlcjpwYXNzd29yZA==。
- 作用:告诉服务器客户端的相关信息,主要用于身份验证。客户端通过这个字段向服务器提供认证信息,证明自己有权访问受保护的资源 。当服务器设置了资源访问权限,要求客户端提供身份凭证时,客户端就会在请求中带上
-
Expect
-
作用:表明客户端要求服务端
做出特定的行为, -
示例:
Expect: 100-continue:客户端告知服务器,希望在发送请求体前,先得到服务器的确认(返回 100 Continue 响应 ),确认服务器已准备好接收请求体。适用于客户端要发送大量数据或需较长时间生成请求正文的场景,提前确认可避免盲目发送,节省带宽和服务器资源。比如客户端向服务器上传大文件时,可先发送带此请求头的请求。并非所有服务器都支持Expect请求头,实际应用中需依服务器支持情况及具体需求决定是否使用
-
-
Expect
-
作用:表明客户端要求服务端
做出特定的行为, -
示例:
Expect: 100-continue:客户端告知服务器,希望在发送请求体前,先得到服务器的确认(返回 100 Continue 响应 ),确认服务器已准备好接收请求体。适用于客户端要发送大量数据或需较长时间生成请求正文的场景,提前确认可避免盲目发送,节省带宽和服务器资源。比如客户端向服务器上传大文件时,可先发送带此请求头的请求。并非所有服务器都支持Expect请求头,实际应用中需依服务器支持情况及具体需求决定是否使用
- Accept-Charset:浏览器可以接受的字符编码集。
- Accept-Ranges:可以请求网页实体的一个或者多个子范围字段。
- Content-Length:请求的内容长度。
- Content-Type:请求的与实体对应的MIME信息。
- Date:请求发送的日期和时间。`
- From:发出请求的用户的Email。
- If-Match:只有请求内容与实体相匹配才有效。
- If-Modified-Since:如果请求的部分在指定时间之后被修改则请求成功,未被修改则返回304代码。
- If-None-Match:如果内容未改变返回304代码,参数为服务器先前发送的Etag,与服务器回应的Etag比较判断是否改变。
- If-Range:如果实体未改变,服务器发送客户端丢失的部分,否则发送整个实体。
- If-Unmodified-Since:只在实体在指定时间之后未被修改才请求成功。
- Max-Forwards:限制信息通过代理和网关传送的时间。
- Pragma:用来包含实现特定的指令。
- Proxy-Authorization:连接到代理的授权证书。
- Range:只请求实体的一部分,指定范围。
- TE:客户端愿意接受的传输编码,并通知服务器接受接受尾加头信息。
- Upgrade:向服务器指定某种传输协议以便服务器进行转换(如果支持。
- User-AgentUser-Agent:的内容包含发出请求的用户信息。
- Via:通知中间网关或代理服务器地址,通信协议。
- Warning:关于消息实体的警告信息
-
常见Http响应头
HTTP 响应头是服务器在向客户端发送响应时包含的附加信息,用于告知客户端关于响应的各种细节和服务器的相关信息。以下是一些常见的 HTTP 响应头:
-
Accept - Ranges
- 作用:告知客户端服务器是否支持范围请求,以及支持的
范围单位。如果服务器支持范围请求,客户端可以请求资源的部分内容,而不是整个资源,这在断点续传或同时请求资源的多个部分时非常有用。 - 示例:
Accept - Ranges: bytes表示服务器支持以字节为单位的范围请求。如果服务器不支持范围请求,则可能返回Accept - Ranges: none。
- 作用:告知客户端服务器是否支持范围请求,以及支持的
-
Age
- 作用:指示响应从服务器生成到客户端接收所经过的时间(以秒为单位)。这个时间是由服务器设置的,它可以帮助客户端了解响应的新鲜度。
- 示例:
Age: 360表示响应已经生成了 360 秒。
-
Cache - Control
- 作用:与请求头中的
Cache - Control类似,用于指定缓存相关的指令,控制客户端和中间缓存如何缓存和使用响应。它可以覆盖默认的缓存策略,指示缓存是否可以存储响应、缓存的有效期以及其他缓存相关的行为。 - 示例:
Cache - Control: public, max - age=3600表示响应可以被公共缓存(如代理服务器)和客户端缓存,缓存有效期为 3600 秒。Cache - Control: no - cache表示不允许缓存,每次都需要从服务器获取最新的响应。
- 作用:与请求头中的
-
Content - Encoding
- 作用:说明响应内容所采用的编码方式,如
gzip、deflate或br等。客户端可以根据这个头信息对响应内容进行相应的解码。 - 示例:
Content - Encoding: gzip表示响应内容使用了gzip压缩算法进行编码,客户端需要使用gzip解压缩工具来还原原始内容。
- 作用:说明响应内容所采用的编码方式,如
-
Content - Language
- 作用:指定响应内容所使用的语言。这有助于客户端根据用户的语言偏好来正确显示内容。
- 示例:
Content - Language: en - US表示响应内容是用美式英语编写的。如果响应包含多种语言的内容,可以列出多个语言标签,如Content - Language: en - US, en - GB, fr - FR。
-
Content - Length
- 作用:表示响应消息体的长度(以字节为单位)。客户端可以根据这个值来确定何时接收完整个响应内容。
- 示例:
Content - Length: 1024表示响应消息体的长度为 1024 字节。对于分块传输的响应,Content - Length头可能不存在,而是使用Transfer - Encoding: chunked头来表示分块传输。
-
Content - Location
- 作用:当资源的实际位置与请求的 URL 不同时,服务器可以使用这个头来指定资源的实际位置。这在资源被移动或重定向到其他位置时很有用。
- 示例:
Content - Location: https://www.example.com/new - location.html表示请求的资源实际上位于指定的新位置。
-
Content - Type
- 作用:指示响应内容的类型,如
text/html表示 HTML 文档,application/json表示 JSON 数据,image/jpeg表示 JPEG 图像等。客户端可以根据这个头信息来正确解析和显示响应内容。 - 示例:
Content - Type: text/html; charset=UTF - 8表示响应内容是 HTML 格式,字符编码为 UTF - 8。Content - Type: application/json表示响应内容是 JSON 格式的数据。Content-Type为application/octet-stream表示响应体是二进制数据,常用于文件下载和二进制数据传输。
- 作用:指示响应内容的类型,如
-
Date
- 作用:表示服务器生成响应的日期和时间。这个时间是按照格林威治标准时间(GMT)表示的,客户端可以根据这个时间来了解响应的生成时间,并进行相关的时间计算或验证。
- 示例:
Date: Tue, 15 Nov 2022 12:00:00 GMT表示服务器在 2022 年 11 月 15 日星期二 12 点整生成了该响应。
-
ETag
- 作用:是一个唯一标识资源内容的标签,用于缓存验证和条件请求。服务器可以根据资源的内容生成一个 ETag 值,客户端在后续请求中可以通过
If - None - Match头将 ETag 值发送给服务器,服务器根据 ETag 值来判断资源是否被修改,如果未修改则返回 304 状态码,告知客户端可以使用缓存的副本。 - 示例:
ETag: "abc123"表示资源的 ETag 值为"abc123"。ETag 值通常是一个由服务器生成的唯一字符串,它基于资源的内容、版本或其他相关信息计算得出。
- 作用:是一个唯一标识资源内容的标签,用于缓存验证和条件请求。服务器可以根据资源的内容生成一个 ETag 值,客户端在后续请求中可以通过
-
Expires
- 作用:指定响应内容的过期时间。客户端在过期时间之前可以使用缓存的响应,过期后则需要重新从服务器获取最新的响应。
- 示例:
Expires: Thu, 01 Dec 2022 12:00:00 GMT表示响应在 2022 年 12 月 1 日星期四 12 点整过期。如果Expires头设置为一个过去的时间,客户端会认为响应已经过期,需要立即从服务器获取新的响应。
-
Location
- 作用:主要用于重定向请求。当服务器返回 3xx 重定向状态码(如 301、302 等)时,
Location头会指定客户端应该重定向到的目标 URL。 - 示例:
Location: https://www.example.com/new - page.html表示客户端需要重定向到指定的新页面。
- 作用:主要用于重定向请求。当服务器返回 3xx 重定向状态码(如 301、302 等)时,
-
Server
- 作用:标识服务器的类型和版本信息。这有助于客户端了解服务器的相关信息,同时也可以帮助服务器管理员进行统计和故障排除。
- 示例:
Server: Apache/2.4.41 (Ubuntu)表示服务器使用的是 Apache 2.4.41 版本,运行在 Ubuntu 操作系统上。
-
Set - Cookie
- 作用:用于在客户端设置 Cookie。服务器可以通过这个头向客户端发送一个或多个 Cookie,客户端会将这些 Cookie 存储在本地,并在后续的请求中发送给服务器。
- 示例:
Set - Cookie: sessionId=123456; expires=Thu, 01 Dec 2022 12:00:00 GMT; path=/表示设置了一个名为sessionId的 Cookie,值为123456,过期时间为 2022 年 12 月 1 日 12 点,路径为根路径/。
-
Transfer - Encoding
- 作用:表示响应内容的传输编码方式,常见的值是
chunked,用于分块传输响应内容。当响应内容的长度在传输开始时无法确定(例如动态生成的内容或流式传输),服务器会使用分块传输编码,将内容分成多个块进行传输,每个块都有自己的长度标识。 - 示例:
Transfer - Encoding: chunked表示采用分块传输编码。客户端会根据块的长度信息来正确接收和组装响应内容。
- 作用:表示响应内容的传输编码方式,常见的值是
web通过什么做到并发请求
- 基于浏览器的并发请求:现代浏览器支持同时发起多个 HTTP 请求。当浏览器解析到一个网页中包含多个资源(如图片、脚本、样式表等)时,会同时发起对这些资源的请求,以加快页面的加载速度。浏览器会根据自身的限制和网络状况,合理地分配并发请求的数量和优先级。例如,Chrome 浏览器通常会对同一个域名同时发起 6 - 8 个请求。
- 使用 AJAX 和 Fetch API:通过 JavaScript 中的 AJAX(Asynchronous JavaScript and XML)或 Fetch API,可以在不刷新页面的情况下向服务器发送异步请求。可以同时发起多个 AJAX 或 Fetch 请求,实现并发数据获取。例如,在一个电商网站中,同时获取商品列表和用户购物车信息。
- Web Workers:Web Workers 允许在后台线程中执行脚本,与主线程并行运行,从而实现并发处理。可以将一些耗时的任务(如数据处理、文件上传等)分配给 Web Workers 执行,不阻塞主线程的 UI 渲染和其他操作。这样在进行多个任务时,能提高整体的响应速度和用户体验。
网络分层模型有哪七层 ⭐️⭐ (重点)
- 应用层(Application):为应用程序提供网络服务接口。
HTTP(网页浏览)、FTP(文件传输)、SMTP(电子邮件) - 表示层(Presentation):数据的 格式转换、加密解密和压缩解压。
SSL/TLS加密、JPEG/MPEG格式转换。 - 会话层(Session):管理 会话(Session) 的建立、维护和终止,支持数据同步和断点续传。
断点续传、会话恢复(如RPC、NetBIOS)。 - 传输层(Transport):提供 进程到进程 的可靠或不可靠传输,负责端口的标识、数据分段、流量控制和差错恢复。
TCP(可靠传输)、UDP(高效无连接传输) - 网络层(Network):实现 端到端 的数据传输,负责逻辑寻址(IP地址)、路由选择(最佳路径)和拥塞控制。
IP协议(IPv4/IPv6)、路由器选择路径IP(网际协议) ICMP (网际控制消息协议) ARP(地址解析协议) RARP(反向地址解析协议) - 数据链路层(Link):将比特组装成帧并实现点到点的传递。
- 物理层(Physical):通过媒介传输比特,确定机械及电气规范。
网络分层模型中的七层指的是 OSI(开放系统互连)模型,它是一个理论框架,用于标准化网络通信功能
实际应用中的简化模型
OSI模型更多用于理论教学,实际广泛使用的是 TCP/IP四层模型,其层级对应关系如下:
- 应用层(对应OSI的应用层、表示层、会话层)
- 传输层
- 网络层
- 网络接口层(对应OSI的数据链路层和物理层)
通过理解OSI七层模型,可以更好地诊断网络问题(如物理层故障、传输层拥塞等),并设计高效协议。
OSI模型(开放系统互联参考模型) 是国际标准化组织(ISO)提出的网络通信框架,用于定义不同设备在网络中通信的标准流程。它将复杂的网络通信过程划分为 七层,每层负责特定功能,且上下层之间通过接口交互。以下是各层的详细解析:
7. 应用层(Application Layer)
- 功能:为应用程序提供网络服务接口,直接面向用户需求(如发送邮件、访问网页)。
- 协议示例:
- HTTP(网页浏览)、FTP(文件传输)、SMTP(邮件发送)。
- DNS(域名解析)、WebSocket(实时通信)。
- 数据单元:消息(Message)。
6. 表示层(Presentation Layer)
- 功能:处理数据的 格式转换,包括加密解密、压缩解压缩、字符编码(如UTF-8)和数据结构转换。
- 典型场景:
- 将服务器返回的JSON数据转换为客户端可读的结构。
- TLS/SSL加密(常与会话层合并实现)。
- 协议示例:SSL/TLS(加密)、JPEG(图像编码)、MPEG(视频编码)。
5. 会话层(Session Layer)
- 功能:管理 会话(Session) 的建立、维护和终止,支持数据同步和断点续传。
- 典型场景:数据库连接、远程登录(SSH)、视频会议。
- 协议示例:NetBIOS、RPC(远程过程调用)。
4. 传输层(Transport Layer)
- 功能:提供 进程到进程 的可靠或不可靠传输,负责端口的标识、数据分段、流量控制和差错恢复。
- 关键协议:
- TCP:面向连接,可靠传输(如文件传输、网页浏览)。
- UDP:无连接,高效但不可靠(如视频流、DNS查询)。
- 数据单元:段(Segment,TCP)/ 数据报(Datagram,UDP)。
- 核心问题:如何保证数据完整性和顺序?如何区分同一设备上的多个应用(端口号)?
3. 网络层(Network Layer)
- 功能:实现 端到端 的数据传输,负责逻辑寻址(IP地址)、路由选择(最佳路径)和拥塞控制。
- 关键设备:路由器(Router)。
- 协议示例:IP(IPv4/IPv6)、ICMP(Ping)、OSPF、BGP。
- 数据单元:数据包(Packet)。
- 核心问题:如何通过IP地址找到目标设备?如何选择最优路径?
2. 数据链路层(Data Link Layer)
- 功能:提供 节点到节点 的可靠传输,将比特流组织为 帧(Frame),并处理物理寻址(MAC地址)、差错检测(如CRC校验) 和流量控制。
- 关键设备:交换机(Switch)、网桥(Bridge)。
- 协议示例:Ethernet(数据链路层)、Wi-Fi(MAC层)、PPP。
- 数据单元:帧(Frame)。
- 核心问题:如何标识设备(MAC地址)?如何检测和纠正传输错误?
1. 物理层(Physical Layer)
- 功能:负责在物理媒介(如电缆、光纤、无线电波)上传输 原始比特流(0和1)。
- 关键设备:网线、集线器(Hub)、中继器(Repeater)、调制解调器(Modem)。
- 协议示例:Ethernet(物理层部分)、USB、蓝牙(物理层)。
- 数据单元:比特(Bit)。
- 核心问题:如何表示0和1?如何建立/断开连接?传输速率如何控制?
数据封装与解封装过程
-
发送端:数据从应用层向下传递,每层添加头部(或尾部)信息。
- 应用层 → 传输层:添加 TCP/UDP头部(含端口号)。
- 传输层 → 网络层:添加 IP头部(含IP地址)。
- 网络层 → 数据链路层:添加 帧头部和尾部(含MAC地址、CRC校验)。
- 数据链路层 → 物理层:转换为比特流。
-
接收端:从物理层向上解析,逐层去除头部信息,最终交付给目标应用。
三种模型对比
实际应用与意义
- 故障排查:通过分层定位问题(如网络层IP配置错误、传输层端口阻塞)。
- 协议设计:明确各层职责,避免功能冗余(如HTTP无需关心数据如何路由)。
- 设备开发:指导硬件/软件设计(如交换机专注数据链路层,路由器处理网络层)。
常见误区
- “OSI模型是实际协议”:OSI是理论模型,实际网络(如互联网)基于 TCP/IP模型。
- “每层必须严格独立”:现实中协议可能跨层实现(如TLS在传输层和应用层之间)。
- “物理层只涉及硬件”:物理层协议也定义电气特性(如电压、频率)。
总结:OSI模型通过分层抽象,简化了网络通信的复杂性。理解各层功能及协作关系,是掌握网络技术(如HTTP优化、网络安全)的基础。
gzip 的原理是什么
gzip 使用了 LZ77 算法与 哈夫曼编码(Huffman) 编码来压缩文件,重复度越高的文件可压缩的空间就越大。
压缩数据
经过 LZ77 算法和哈夫曼编码处理后,会生成压缩后的数据。Gzip 会在压缩数据的头部添加一些元信息,如文件格式、压缩方法、时间戳等,然后将这些信息和压缩数据一起输出。
解压数据
解压缩是压缩的逆过程,主要步骤如下:
- 读取元信息:从压缩数据的头部读取元信息,包括文件格式、压缩方法等。
- 哈夫曼解码:根据哈夫曼编码表将压缩数据解码为经过 LZ77 算法处理后的数据。
- LZ77 解码:根据三元组(偏移量,长度,下一个字符)将经过 LZ77 算法处理后的数据还原为原始数据。
什么是Etag ⭐️⭐
ETag(Entity Tag,实体标记)是 HTTP 协议中用于资源缓存验证的一种机制,由服务器生成并返回给客户端,用于唯一标识资源版本。当资源内容发生变化时,ETag 会随之改变,客户端可以通过 ETag 判断本地缓存是否过期,从而减少不必要的重复数据传输,提升性能和效率。
ETag 的核心作用
-
缓存验证
客户端(如浏览器)在首次请求资源时,服务器会返回资源的 ETag 值。
当客户端再次请求同一资源时,会通过If-None-Match请求头将缓存的 ETag 发送给服务器。- 如果服务器上的资源 ETag 未变化,返回
304 Not Modified(无需重新下载资源)。 - 如果 ETag 已变化,返回
200 OK和新资源内容。
- 如果服务器上的资源 ETag 未变化,返回
-
防止覆盖冲突(部分场景)
在并发修改资源的场景中(如 API 更新),客户端通过If-Match请求头携带 ETag,服务器可验证资源是否被他人修改过,避免数据覆盖。
ETag 的工作原理
-
首次请求
GET /index.html HTTP/1.1 Host: example.com服务器响应:
HTTP/1.1 200 OK Content-Type: text/html ETag: "123abc" Content-Length: 1024 -
后续请求(带缓存验证)
GET /index.html HTTP/1.1 Host: example.com If-None-Match: "123abc"- 若资源未修改,服务器返回:
HTTP/1.1 304 Not Modified ETag: "123abc" - 若资源已修改,服务器返回新内容和新的 ETag。
- 若资源未修改,服务器返回:
ETag 的生成方式
服务器根据资源内容或元数据生成 ETag,常见方法包括:
- 哈希值:如对文件内容计算 MD5、SHA-256 等哈希值(例如
ETag: "d41d8cd98f00b204e9800998ecf8427e")。 - 版本号:如基于资源修改时间戳或数据库版本号(例如
ETag: "v2.0")。 - 组合值:结合文件大小、修改时间等生成唯一标识。
ETag 与 Last-Modified 的区别
ETag 高于 Last-Modified(若同时存在)
| 特性 | ETag | Last-Modified |
|---|---|---|
| 精度 | 更精确(可检测内容微小变化) | 基于时间(秒级,可能不准确) |
| 生成方式 | 灵活(哈希、版本号等) | 依赖文件的最后修改时间 |
| 适用场景 | 动态内容、频繁修改的资源 | 静态文件 |
| 优先级 | 高于 Last-Modified(若同时存在) | 次优先级 |
ETag 的注意事项
-
性能问题
- 计算哈希值可能消耗服务器资源(尤其是大文件)。
- 解决方案:对静态文件使用弱 ETag(以
W/开头,如ETag: W/"123abc"),仅验证资源是否大致相同。
-
分布式系统的挑战
- 若资源存储在多个服务器上,需确保 ETag 生成规则一致,否则可能导致缓存失效。
- 例如:不同服务器对同一文件生成不同的 ETag。
总结
ETag 是 HTTP 缓存机制的关键组成部分,通过唯一标识资源版本,帮助客户端智能判断是否需要重新下载数据。合理使用 ETag 能显著减少网络流量、提升用户体验,但需注意生成方式和服务端配置的一致性。
http 响应头中的 ETag 值是如何生成的?
- 客户端(如浏览器)在首次请求资源时,服务器会返回资源的 ETag 值。
- 当客户端再次请求同一资源时,会通过
If-None-Match请求头将缓存的 ETag 发送给服务器。 - 如果服务器上的资源 ETag 未变化,返回
304 Not Modified(无需重新下载资源)。如果 ETag 已变化,返回200 OK和新资源内容。
如果 http 响应头中 ETag 值改变了,是否意味着文件内容一定已经更改?
HTTP 响应头中的 ETag(实体标签)值改变,并不一定意味着文件内容已经更改。具体原因如下:
- ETag 值改变 ≠ 文件内容一定变化,需区分强/弱 ETag 及服务器生成策略。
- 强 ETag 可靠:内容变化必导致 ETag 变化。
- 弱 ETag 不可靠:ETag 变化可能仅反映元数据或服务器状态变化。
- 最佳实践:通过条件请求(
If-None-Match)验证资源是否实际更新
1. ETag 的生成方式决定其可靠性
- 强 ETag(不以
W/开头):- 通常基于文件内容的哈希值(如 MD5、SHA-1),或唯一标识符(如版本号)。
- 内容变化必然导致 ETag 变化。
- 示例:
ETag: "123456789"。
- 弱 ETag(以
W/开头):- 基于非内容属性(如文件修改时间、大小或元数据)。
- 内容未变时,ETag 仍可能变化。
- 示例:
ETag: W/"123456789"。
2. 如何验证内容是否实际变化?
- 使用
If-None-Match条件请求:- 客户端发送请求时携带
If-None-Match: <ETag>,服务器会对比 ETag:- 匹配 → 返回
304 Not Modified(内容未变)。 - 不匹配 → 返回
200 OK和新内容(内容已变)。
- 匹配 → 返回
- 客户端发送请求时携带
- 结合
Last-Modified头:- 双重验证修改时间和 ETag,提高准确性。
3. 实际应用中的建议
- 优先使用强 ETag:
- 确保 ETag 基于内容哈希(如
ETag: "sha256-xxxx")。
- 确保 ETag 基于内容哈希(如
- 避免依赖弱 ETag 判断内容变化:
- 弱 ETag(
W/)可能误导客户端缓存逻辑。
- 弱 ETag(
formData和原生的ajax有什么区别
formData与普通的Ajax相比,非常适合用于上传文件。你可以直接把文件对象添加到 FormData 中,它会自动处理文件的上传,无需额外的编码。Ajax处理文件上传相对复杂。你需要手动设置请求头为 multipart/form-data,并且处理文件的二进制数据。
介绍下表单提交,和formData有什么关系
这是HTML5 中新增的API
优点:FormData不仅能读取表单数据,也能自行追加数据
http 1.1 中的 keep-alive 有什么作用
在 http 1.1 中,在响应头中设置 keep-alive ,允许在同一个 TCP 连接上发送多个 HTTP 请求和响应,避免了频繁建立和关闭 TCP 连接的开销
减少 TCP 连接建立和关闭的开销
- 原理:在 HTTP 1.0 协议里,每个 HTTP 请求都需要建立一个新的 TCP 连接,请求完成后该连接就会关闭。而建立 TCP 连接时,需要经过三次握手,关闭连接时要经过四次挥手,这些过程都会带来一定的时间和资源开销。HTTP 1.1 的
Keep - Alive机制允许在同一个 TCP 连接上发送多个 HTTP 请求和响应,避免了频繁建立和关闭 TCP 连接的开销。 - 示例:当你访问一个包含多个资源(如图片、CSS 文件、JavaScript 文件)的网页时,如果没有
Keep - Alive,浏览器需要为每个资源建立一个新的 TCP 连接,这会显著增加页面的加载时间。启用Keep - Alive后,浏览器可以通过同一个 TCP 连接依次请求这些资源,减少了连接建立和关闭的时间。
提高服务器和客户端的性能
- 服务器端:对于服务器而言,频繁地建立和关闭 TCP 连接会消耗大量的系统资源,如 CPU 时间和内存。使用
Keep - Alive可以减少这些资源的消耗,让服务器能够处理更多的请求。服务器可以在一个连接上持续为客户端提供服务,避免了频繁的上下文切换和资源分配。 - 客户端:在客户端方面,减少 TCP 连接的建立和关闭可以降低网络延迟,提高页面的加载速度。客户端可以更快地获取到所需的资源,提升用户体验。
实现管道化请求
- 原理:
Keep - Alive为 HTTP 1.1 的管道化请求提供了基础。管道化请求允许客户端在一个 TCP 连接上连续发送多个请求,而不必等待前一个请求的响应。服务器会按照请求的顺序依次处理这些请求,并依次返回响应。这样可以进一步提高数据传输的效率,减少网络延迟。 - 示例:假设客户端需要请求三个资源 A、B、C。在没有管道化请求的情况下,客户端需要先发送请求 A,等待服务器响应后再发送请求 B,以此类推。而启用管道化请求后,客户端可以一次性发送请求 A、B、C,服务器处理完后依次返回响应,节省了等待时间。
综上所述,HTTP 1.1 中的 Keep - Alive 机制通过减少 TCP 连接的建立和关闭开销、提高服务器和客户端的性能以及支持管道化请求,有效地提升了 HTTP 通信的效率和性能。不过,Keep - Alive也并非适用于所有场景,长时间保持连接可能会占用服务器资源,对于一些短期的、一次性的请求,可能并不需要使用Keep - Alive。
URI,URL,URN的区别
总结
- URI 是广义的资源标识符,包含 URL 和 URN。
- URL 通过位置定位资源(“资源在哪,如何访问”)。
- URN 通过名称标识资源(“资源叫什么”,与位置无关)。
URI 是一种语义上的抽象概念,可以是绝对的,也可以是相对的,而URL则必须提供足够的信息来定位,是绝对的。
URI、URL 和 URN 是用于标识资源的三个关键术语,它们之间既有联系又有区别。以下是它们的核心差异及关系:
1. URI(Uniform Resource Identifier,统一资源标识符)
- 定义:
URI 是唯一标识一个资源的字符串,是 URL 和 URN 的超集。 - 作用:
提供一种标准方式标识资源 (如文档、图像、服务等),不一定是通过位置或名称,但必须唯一。 - 示例:
https://www.example.com/page.html(URL)urn:isbn:9780141036144(URN)mailto:user@example.com(既是 URI 也是 URL)
2. URL(Uniform Resource Locator,统一资源定位符)
- 定义:
URL 是 URI 的子集,通过位置和访问方式定位资源,明确告诉用户如何获取资源。 - 结构:
[协议]://[域名]:[端口]/[路径]?[查询参数]#[片段]- 协议:如
http、https、ftp。 - 域名:资源所在服务器地址。
- 路径:资源在服务器上的位置。
- 协议:如
- 示例:
https://www.google.com/search?q=uriftp://files.example.com/data.zip
3. URN(Uniform Resource Name,统一资源名称)
- 定义:
URN 是 URI 的另一种子集,通过名称标识资源,与资源的位置无关。 - 特点:
- 唯一且持久,即使资源位置变化,URN 仍有效。
- 格式以
urn:开头,后接命名空间和标识符。
- 示例:
urn:isbn:0451450523(标识一本国际标准书号的书)urn:uuid:6e8bc430-9c3a-11d9-9669-0800200c9a66(通用唯一标识符)
三者的关系
- URI = URL + URN + 其他标识方式
URI ├── URL(通过位置定位资源) └── URN(通过名称标识资源)
实际应用场景
- URL 的典型使用:
- 访问网页:
https://www.wikipedia.org - 下载文件:
ftp://files.example.com/data.zip
- 访问网页:
- URN 的典型使用:
- 永久标识书籍:
urn:isbn:9780141036144(即使书被移动到不同服务器,URN 不变)。 - 标识标准化文档:
urn:ietf:rfc:7230(IETF 的 RFC 文档)。
- 永久标识书籍:
- URI 的广义性:
mailto:user@example.com(既是 URI 也是 URL)tel:+1234567890(URI 标识电话号码资源)
常见误区
- URI ≠ URL:
URL 是 URI 的子集,所有 URL 都是 URI,但并非所有 URI 都是 URL(例如 URN)。 - URN 未被广泛使用:
URN 在特定领域(如图书管理、标准化文档)有应用,但在 Web 开发中更常见 URL。 - URI 的格式灵活性:
URI 可以是非 URL 或 URN 的其他形式,只要唯一标识资源即可(例如data:text/plain;base64,SGVsbG8=表示一段 Base64 编码的数据)。
常用的HTTP方法有哪些?⭐️⭐️(重点)
HTTP 协议定义了多种请求方法(HTTP Methods),用于指示客户端希望服务器对资源执行的操作。以下是常见 HTTP 请求方法的详细说明,涵盖其用途、安全性、幂等性及实际应用场景:
一、核心方法
1. GET
- 用途:请求指定资源(如获取网页、图片、API 数据)。
- 特点:
- 安全:不修改服务器资源(仅读取)。
- 幂等:多次请求结果相同。
- 可缓存:响应可被浏览器、CDN 缓存。
- 示例:
GET /api/users/123 HTTP/1.1 - 注意:
- 参数通过 URL 查询字符串传递(如
?name=John),长度受浏览器限制(约 2KB~4KB)。 - 不适合传输敏感数据(参数暴露在 URL 中)。
- 参数通过 URL 查询字符串传递(如
2. POST
- 用途:提交数据(如提交表单、上传文件)。
- 特点:
- 非安全:可能修改服务器资源(如创建订单)。
非幂等:多次提交可能产生不同结果(如重复创建资源)。- 不可缓存:
默认不缓存,需显式设置缓存头。
- 示例:
POST /api/users HTTP/1.1 Content-Type: application/json {"name": "John", "age": 30} - 注意:
- 数据通过请求体传输,支持多种格式(如 JSON、表单数据)。
- 适合传输敏感信息(配合 HTTPS)。
3. PUT
- 用途:完整更新资源(
替换整个资源实体)。 - 特点:
- 非安全:修改服务器资源。
- 幂等:多次更新结果一致(用同一数据多次 PUT 效果相同)。
- 示例:
PUT /api/users/123 HTTP/1.1 Content-Type: application/json {"name": "John", "age": 31} - 注意:
- 需提供资源的完整表示,未指定的字段可能被置空。
- 若资源不存在,某些实现会创建新资源(取决于 API 设计)。
- 区分:PUT方法是从客户端向服务器传送的数据取代指定的文档的内容。PUT方法的本质是幂等的方法,通过服务是否是幂等来判断用PUT 还是 POST更合理,通常情况下这两种方法并没有刻意区分,根据语义使用即可
4. DELETE
- 用途:删除指定资源。
- 特点:
- 非安全:修改服务器资源。
- 幂等:多次删除同一资源结果相同(第一次删除后资源不存在,后续返回 404)。
- 示例:
DELETE /api/users/123 HTTP/1.1 - 结果:
- 200 (OK) - 删除成功,同时返回已经删除的资源
- 202 (Accepted)删除请求已经接受,但没有被立即执行(资源也许已经被转移到了待删除区域)
- 204 (No Content) 删除请求已经被执行,但是没有返回资源(也许是请求删除不存在的资源造成的)
5. PATCH
- 用途:部分更新资源(
仅修改指定字段)。 - 特点:
- 非安全:修改服务器资源。
- 幂等性依赖实现:需确保多次相同请求结果一致。
- 示例:
PATCH /api/users/123 HTTP/1.1 Content-Type: application/json {"age": 31} - 注意:
- 需明确指定更新字段(如 JSON Merge Patch 或 JSON Patch 格式)。
比 PUT 更高效,适合频繁小规模更新。
二、辅助方法
6. HEAD
- 用途:获取资源响应头,
不返回响应体。 - 特点:
- 安全:仅读取元数据(如
Content-Length、Last-Modified)。 - 幂等:与 GET 类似,但不返回数据。
- 安全:仅读取元数据(如
- 应用场景:
- 检查资源是否存在(如预验证链接有效性)。
- 监控资源是否更新(通过
Last-Modified或ETag)。
7. OPTIONS
- 用途:获取服务器支持的 HTTP 方法或跨域预检请求(CORS Preflight)。
- 示例:
OPTIONS /api/users HTTP/1.1 Host: example.com Access-Control-Request-Method: POST Access-Control-Request-Headers: Content-Type - 响应示例:
HTTP/1.1 200 OK Allow: GET, POST, PUT, DELETE Access-Control-Allow-Methods: GET, POST, PUT Access-Control-Allow-Headers: Content-Type
8. TRACE
- 用途:诊断请求路径(返回客户端发送的原始请求)。
- 特点:
- 用于测试或调试,可能暴露敏感信息(如 Cookie)。
- 默认被浏览器禁用,需服务器显式支持。
9. CONNECT
- 用途:
建立与目标资源的隧道连接(用于 HTTPS 代理)。 - 示例:
CONNECT example.com:443 HTTP/1.1 Host: example.com:443
三、对比总结 掌握 7种
| 方法 | 安全 | 幂等 | 缓存 | 典型场景 |
|---|---|---|---|---|
| GET | 是 | 是 | 是 | 获取资源、搜索 |
| POST | 否 | 否 | 否 | 创建资源、提交表单 |
| PUT | 否 | 是 | 否 | 替换整个资源 |
| DELETE | 否 | 是 | 否 | 删除资源 |
| PATCH | 否 | 依赖 | 否 | 部分更新资源 |
| HEAD | 是 | 是 | 是 | 获取元数据 |
| OPTIONS | 是 | 是 | 否 | 跨域预检、查看支持的方法 |
五、常见误区
- 误用 GET 提交数据:
导致参数暴露在 URL 中,可能被日志记录或浏览器历史泄露。 - 滥用 POST:
所有操作都用 POST,违背 RESTful 设计原则,降低可维护性。 - 忽略幂等性:
多次调用非幂等方法(如 POST)可能产生重复数据或副作用。 - 安全性:
GET和HEAD不应修改资源,避免副作用。 - 兼容性:
部分旧客户端或服务器可能不支持PATCH或OPTIONS。
可能的延伸考察
请求体与响应体
- 哪些方法通常不携带请求体?(如 GET、DELETE)
- 哪些方法可以/需要携带请求体?(如 POST、PUT、PATCH)
对比和实际使用中的陷阱
- PUT/POST 混用的场景(如某些系统用 POST 实现更新)
- DELETE 是否要有请求体?(规范允许,但不推荐)
状态码与方法配合
- DELETE 成功一般返回什么状态码?(如 204 No Content)
- POST 创建资源后返回什么?(201 Created)
结合安全问题
- POST/PUT 方法可能面临哪些安全风险?如 CSRF?比如修改用户密码、提交虚假订单、更改个人信息等
为什么建议对非幂等方法加 CSRF 防护?
- 其操作具有不可挽回的实质性危害,且是 CSRF 攻击的主要目标,浏览器默认防护不足。(如提交订单、支付、修改密码、绑定手机、发布内容),几乎都是通过 POST(非幂等)实现的,部分特殊 PUT 操作也具备非幂等性。一旦被 CSRF 攻击,会创建重复资源(如生成无效订单、扣取多次款项),这些危害无法通过 “撤销请求” 恢复
哪些方法是幂等的? 哪些方法是安全的?
核心定义
- 幂等性:同一请求调用 1 次或多次,服务器最终状态一致。(调用多次结果一致)
- 安全性:请求仅读取资源,不对服务器产生任何修改 / 删除 / 新增等副作用。(不会对服务器资源产生副作用)
关键结论
- 安全的方法一定是幂等的,幂等的方法不一定是安全的。
- POST 既不幂等,也不安全;GET/HEAD/OPTIONS 既幂等又安全;PUT/DELETE 仅幂等,不安全。
精简对照表
| HTTP 方法 | 幂等性 | 安全性 |
|---|---|---|
| GET | ✅ | ✅ |
| HEAD | ✅ | ✅ |
| OPTIONS | ✅ | ✅ |
| PUT | ✅ | ❌ |
| DELETE | ✅ | ❌ |
| POST | ❌ | ❌ |
补充要点
- 幂等性关注最终状态,而非响应内容(如 DELETE 首次 200,后续 404,仍幂等)。
- 安全性的核心是只读不修改,与调用次数无关。
get和post有什么区别 主要6点
总结
- GET:适合获取数据,参数可见、长度受限、相对不安全、可缓存、幂等。
- POST:适合提交数据,参数隐蔽、长度灵活、相对安全、不缓存、非幂等。
- get在浏览器回退时是无害的,而post会再次提交请求。(记住)
- get产生的url地址可以被收藏,而post不可以。
- get请求参数会被完整保留在浏览器历史记录里,而post中的参数不会被保留。(记住)
get请求只能进行url编码,而post支持多种编码方式。- 对参数的数据类型,get只接受ASCII字符,而post没有限制。
- get比post更不安全,因为参数直接暴露在url上,所以不能用来传递敏感信息。
- get参数通过url传递,post放在request body 中。(记住)
GET 和 POST 是 HTTP 协议中最常用的两种请求方法,它们在设计目的、数据传递方式、安全性、缓存行为等方面有显著区别。以下是详细对比:
2. 数据传递方式
-
GET
- 参数通过 URL 的查询字符串(Query String) 传递,格式为:
https://example.com?name=John&age=30 - 数据可见在地址栏中,可能被浏览器历史、服务器日志记录。
- 参数通过 URL 的查询字符串(Query String) 传递,格式为:
-
POST
- 数据对用户不可见(
但仍可能被网络抓包工具捕获)。
- 数据对用户不可见(
-
区别
- 对于GET方式的请求,浏览器会把http header和data一井发送出去,服务器响应200(返回数据)。对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok。
并不是所有浏览器都会在POST中发送两次包,Firefox就只发送一次。
- 对于GET方式的请求,浏览器会把http header和data一井发送出去,服务器响应200(返回数据)。对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok。
3. 数据长度限制
- GET
- URL 长度受限(不同浏览器限制不同,通常为 2048~4096 字符)服务器处理长URL要消耗比较多的资源,为了
性能和安全考虑,会给URL长度加限制 。 - 超出长度可能导致截断或请求失败。
- URL 长度受限(不同浏览器限制不同,通常为 2048~4096 字符)服务器处理长URL要消耗比较多的资源,为了
- POST
- 理论上无限制,但服务器可能配置最大请求体大小(如 Nginx 默认 1MB)。
- 适合传输大量数据(如文件上传)。
4. 安全性
- GET
- 参数暴露在 URL 中,可能被他人查看或篡改(如通过浏览器历史、代理服务器)。
- 不适合传输敏感信息(如密码、Token)。
- POST
- 参数在请求体中,相对隐蔽,但并非绝对安全(
需配合 HTTPS 加密)。 - 更适用于敏感数据提交。
- 参数在请求体中,相对隐蔽,但并非绝对安全(
5. 缓存与浏览器行为
-
GET
- 默认支持并优先缓存(浏览器、CDN、代理服务器等)。将缓存数据与请求 URL 绑定,后续相同 URL 的 GET 请求会先读取本地缓存(未过期则不发送请求到服务器),过期才会重新发起请求。
- 支持书签保存(URL 包含完整参数)。
- 浏览器刷新时可能重复发送请求(但幂等性保证安全性)。
-
POST
- 默认不缓存(
需显式设置缓存头)。浏览器默认不会缓存 POST 请求的响应结果,无论是否相同请求,每次都会将完整请求发送到服务器,获取最新响应数据;如需缓存 POST,需手动配置复杂的响应头(如Cache-Control、ETag),且兼容性差、效果有限。 - 无法保存为书签(无固定 URL)。
- 浏览器刷新时会提示“重新提交表单”。
- 默认不缓存(
-
问题
- 为什么 GET 更适合缓存?
- 表单默认提交方式是什么?(GET 或 POST)
- GET 请求能携带 body 吗?(规范上不能,但部分浏览器允许)
- 为什么 GET 更适合缓存?
- 语义匹配:GET 是只读获取,数据稳定,适配缓存复用;POST 是提交修改,响应唯一,缓存无意义。
- 标识唯一:GET 以 URL 为天然缓存键,匹配成本低;POST 参数在请求体,缓存标识难生成、解析成本高。
- 无副作用:GET 安全幂等,缓存复用无额外影响;POST 非幂等,缓存易引发重复提交等副作用。
- 实现简便:GET 浏览器默认缓存,零配置;POST 需手动配置响应头,实现复杂、兼容性差。
6. 幂等性与副作用
- GET
- 幂等:多次请求对服务器状态无影响(如查询操作)。
- 仅用于读取数据,不修改服务器资源。
- POST
- 非幂等:多次请求可能产生不同结果(如创建多个订单)。
- 用于修改服务器资源(增删改操作)。
7. 适用场景
- 用 GET 的场景
- 搜索查询(参数在 URL 中,可分享链接)。
- 分页加载数据(如
/api/users?page=2)。 - 获取静态资源(图片、CSS、JS)。
- 用 POST 的场景
- 用户登录(提交用户名和密码)。
- 文件上传或表单提交(大数据量)。
创建/更新资源(如发布文章、修改用户信息)。
常见误区
- POST 比 GET 安全
- 错!POST 数据在请求体中,但未加密时仍可能被窃听。必须使用 HTTPS 保障安全。
- GET 不能发送数据
- 错!GET 可以通过 URL 参数发送数据,但受长度限制。
- POST 不能缓存
- 错!
可通过响应头Cache-Control显式控制缓存,但一般不推荐。
- 错!
实际开发中应根据语义(RESTful 规范)和需求选择方法,敏感数据务必通过 HTTPS 传输。
是否可以用 POST 替代 GET?
- 技术可行性:POST 可以替代 GET 实现数据查询功能;
- 工程合理性:不推荐无脑替代,违背 HTTP 语义且存在缓存、可追溯性等诸多弊端;
- 核心原则:贴合 HTTP 语义使用,按「是否修改服务器状态」选择对应请求方法,特殊场景特殊处理。
哪些方法属于“简单请求”?
满足 全部以下条件 的 HTTP 请求,即为简单请求:
- 请求方法:仅限
GET、POST、HEAD三种。 - 请求头:仅包含浏览器自动设置的头,或手动设置的以下头:
Accept、Accept-Language、Content-Language、Content-Type(仅限application/x-www-form-urlencoded、multipart/form-data、text/plain三种值)。 - 无自定义头:不包含任何开发者自定义的 HTTP 头(如
Authorization、Token等)。
使用 PUT/PATCH/DELETE 会触发预检请求(OPTIONS),为什么?
核心:跨域安全策略的限制。
- 这类方法属于 非简单请求方法,超出了浏览器对「跨域简单交互」的安全限制。
- 其语义是修改 / 删除服务器资源,具有强副作用,若直接跨域执行,可能引发安全风险(如恶意修改数据)。
- 因此,浏览器会先发送 预检请求(OPTIONS) ,向服务器询问:是否允许当前域名使用该方法进行跨域请求。
- 只有服务器返回明确的允许响应(如包含
Access-Control-Allow-Methods等头),浏览器才会发送真正的 PUT/PATCH/DELETE 请求;否则,直接拦截,不执行后续操作。
post 请求为什么会多发送一次option请求 ⭐️⭐⭐️
核心结论:跨域场景下,当 POST 请求被浏览器判定为「非简单请求」时,会先发送 OPTIONS 预检请求,再发送实际的 POST 请求。
触发场景
仅发生在跨域 POST 请求中,且满足以下任一条件:
- 请求属于「非简单方法」(虽 POST 本身可能是简单方法,但携带自定义头、非标准 Content-Type 时,会被判定为非简单请求);
- 携带自定义 HTTP 头部;
- 使用了如
application/json这类非简单的请求体格式。
目的:浏览器提前向服务器确认「是否允许该跨域操作」,避免非安全请求对服务器造成意外修改。
流程:OPTIONS 请求携带待校验的方法、头信息 → 服务器通过 Access-Control-* 响应头告知允许的范围 → 协商通过后,浏览器才会发送实际的 POST 请求。
补充说明(避坑点)
- 若 POST 请求是跨域简单请求(如 Content-Type 为
application/x-www-form-urlencoded、无自定义头),不会触发 OPTIONS 预检。 - 同域请求永远不会触发 OPTIONS 请求,这是跨域独有的机制。
浏览器存储数据方式有哪些 ⭐️⭐⭐️ (重点)
存储方案对比表
| 存储方式 | 容量 | 生命周期 | 适用场景 | 安全性 |
|---|---|---|---|---|
| Cookie | ≤4KB/个 | 手动或自动过期 | 会话管理、跟踪 | 需配合 Secure/HttpOnly |
| LocalStorage | ~5MB | 持久存储 | 用户偏好、非敏感数据 | 易受 XSS |
| SessionStorage | ~5MB | 标签页关闭清除 | 临时数据(如表单页跳转) | 同 LocalStorage |
| IndexedDB | 大(GB 级) | 持久存储 | 大量结构化数据、离线应用 | 需自行管理加密 |
| Service Worker | 动态 | 手动清理或浏览器策略 | 静态资源缓存、离线支持 | 资源需同源 |
要判断这些存储方案是否需要同源,需逐一分析:
- Cookie:默认遵循同源策略,且还受域名、路径、过期时间等限制;不过可通过设置
domain属性实现跨子域共享。 - LocalStorage:严格遵循同源策略,只有协议、域名、端口完全相同的页面,才能共享 LocalStorage 数据。
- SessionStorage:同样遵循同源策略,且仅在同一浏览器窗口或标签页的同一会话中,同源页面可共享,关闭窗口或标签页后数据销毁。
- IndexedDB:遵循同源策略,只有同源的页面才能访问和操作同一 IndexedDB 数据库。
- Service Worker:注册时要求脚本文件同源,且其控制的页面也需同源,以保证资源和数据的安全性。
综上,Cookie、LocalStorage、SessionStorage、IndexedDB、Service Worker 都需要遵循同源策略,只是 Cookie 在跨子域场景下可通过配置灵活处理,其余均为严格的同源限制。
1. Cookie 实操 保证cookie的安全
- 用途:
- 存储会话状态(如用户登录凭证)。
- 跟踪用户行为(如广告偏好)。
- 特点:
- 容量限制:每个 Cookie ≤ 4KB,同域名下总数有限(通常 ≤ 50个)。
- 自动携带:每次 HTTP 请求会附加同域 Cookie,可能影响性能。
- 过期控制:通过
Expires或Max-Age设置有效期。
- 安全性:
- 设置
Secure属性仅通过 HTTPS 传输。 - 设置
HttpOnly防止 JavaScript 访问(防 XSS)。 - 通过
SameSite限制跨站请求携带 Cookie(防 CSRF)。
- 设置
- 示例:
document.cookie = "username=John; expires=Thu, 18 Dec 2025 12:00:00 UTC; path=/; Secure; HttpOnly";
2. LocalStorage 和 SessionStorage
- 用途:
- 持久化存储简单键值对数据(如用户偏好设置)。
- 特点:
- 容量:约 5MB 每域名(不同浏览器有差异)。
- 作用域:同源策略,
不同标签页共享 LocalStorage,SessionStorage仅限当前标签页。 - 生命周期:
LocalStorage:手动清除或通过代码删除。SessionStorage:标签页关闭后自动清除。
- API:
localStorage.setItem("theme", "dark"); // 存 let theme = localStorage.getItem("theme"); // 取 localStorage.removeItem("theme"); // 删 - 安全性:
- 易受 XSS 攻击(可通过 JavaScript 直接读写)。
- 不适合存储敏感信息(如密码、Token)。
3. IndexedDB 实操
- 用途:
- 存储大量结构化数据(如离线应用数据、用户文件缓存)。
- 支持复杂查询和事务操作。
- 特点:
- 容量:动态分配,通常为磁盘空间的 50% 以上。
- 异步 API:非阻塞主线程,适合处理大量数据。
- 索引支持:可通过键、索引快速检索数据。
- 示例:
// 打开数据库 let request = indexedDB.open("myDB", 1); request.onsuccess = (event) => { let db = event.target.result; let transaction = db.transaction("books", "readwrite"); let store = transaction.objectStore("books"); store.add({ id: 1, title: "JavaScript指南", price: 99 }); };
4. Service Worker 缓存
- 用途:
- 缓存网络请求资源(如 HTML、CSS、JS、图片),实现离线访问。
- PWA(渐进式 Web 应用)的核心技术。
- 特点:
- API 属于 Service Worker:需注册 Service Worker 后使用。
- 精细控制缓存策略:可自定义缓存匹配、更新逻辑。
- 示例:
// Service Worker 中缓存资源 self.addEventListener("fetch", (event) => { event.respondWith( caches.match(event.request).then((response) => { return response || fetch(event.request); }) ); });
5. Web Workers 中的存储
- SharedArrayBuffer:
- 允许多个 Web Worker 共享内存,用于高性能计算。
- 需配合
Atomics实现线程安全。
- 限制:
- 受跨域隔离策略限制(需设置 COOP/COEP 头)。
选择建议
- 小数据 & 自动携带 → Cookie(注意安全设置)。
- 简单键值对 & 持久化 → LocalStorage。
- 大量结构化数据 → IndexedDB。
- 离线资源缓存 → Service Worker。
- 敏感信息 →
避免前端存储,优先使用后端 Session + HttpOnly Cookie。
安全注意事项
- 防 XSS:避免存储敏感数据在前端,或严格过滤输入输出。
- 防 CSRF:关键操作使用 CSRF Token + SameSite Cookie。
- 加密存储:必要时对数据加密(如使用
Web Crypto API)。 - 隐私模式:部分浏览器在隐私模式下会限制存储或清除数据。
合理选择存储方案,结合业务需求和安全规范,可显著提升应用性能和用户体验。
介绍localstorage的API
localStorage是一个用来做本地持久化存储的Web API。 localStorage以键值对的形式存储数据。用法很简单, localStorage.clear() 移除所有
// 设置
localStorage.setItem('myCat', 'Tom');
// 获取
let cat = localStorage.getItem('myCat');
// 移除
localStorage.removeItem('myCat');
// 移除所有
localStorage.clear();
监听LocalStorage变化:localStorage被改变时(从无到有,从有到无,值改变),会触发一个storage事件。我们可以在window上监听到这个事件。
window.addEventListener('storage', () => {
...
});
window.onstorage = () => {
...
};
这里需要注意的是,在改变localStorage的当前页面,是无法监听到storage事件的。如果我同时打开了多个同源的页面: A页面、B页面、C页面,当在A页面中修改localstorage时,B页面和C页面中都可以监听到storage事件,而A页面是不会触发storage事件的。实操
有几个点需要注意
- localStorage是
以源(origin)为维度进行存储的。也就是说,跨域访问其他站点的localStorage是行不通的。 - localStorage是
以字符串的形式保存数据的。 - 对于每一个域,localStorage最多允许存储几M数量级的数据 (具体数字因浏览器而异)。
localStorage可以用来做什么
- 存储登录token,
- 用户信息等数据;
- 保存业务数据;
- 保存代码,以提高网站性能
IndexedDB是什么
IndexedDB 是浏览器提供的一种高性能、非关系型数据库,专为存储大量结构化数据设计,支持复杂的查询和事务操作。它是现代 Web 应用中处理离线数据、缓存复杂结构的核心技术。以下是 IndexedDB 的核心要点和实战指南:
一、IndexedDB 的核心特点
- 大容量存储
- 存储容量动态分配,通常可达磁盘空间的 50% 以上(如 Chrome 默认允许至少 1GB)。
- 异步 API
- 所有操作异步执行,避免阻塞主线程,适合处理大量数据。
- 事务支持
- 支持原子性操作,确保数据一致性(如转账操作)。
- 索引与游标
- 可通过索引快速查询数据,游标支持遍历大量数据。
- 同源策略
- 数据按域名隔离,不同源的页面无法访问。
二、核心概念
1. 数据库(Database)
- 每个域名可创建多个数据库,通过唯一名称标识。
- 版本控制:数据库结构变更(如新增表)需升级版本。
2. 对象仓库(Object Store)
- 类似数据库中的“表”,用于存储 JavaScript 对象(键值对)。
- 主键(Key):每个对象必须有唯一主键,支持自增(
autoIncrement)。
3. 索引(Index)
- 基于对象属性创建索引,加速查询(类似 SQL 索引)。
- 可定义唯一性约束(如唯一用户名)。
4. 事务(Transaction)
- 所有读写操作必须在事务中进行,支持原子性提交或回滚。
- 事务模式:
readonly(默认)、readwrite、versionchange。
三、基础操作示例
1. 打开/创建数据库
const request = indexedDB.open('myDatabase', 1); // 名称 + 版本号
request.onupgradeneeded = (event) => {
// 首次创建或版本升级时触发
const db = event.target.result;
// 创建对象仓库(表)
const store = db.createObjectStore('books', {
keyPath: 'id', // 主键字段
autoIncrement: true // 主键自增
});
// 创建索引(基于书名)
store.createIndex('titleIndex', 'title', { unique: false });
};
request.onsuccess = (event) => {
const db = event.target.result;
// 数据库操作...
};
request.onerror = (event) => {
console.error('数据库打开失败:', event.target.error);
};
2. 新增数据
const transaction = db.transaction('books', 'readwrite');
const store = transaction.objectStore('books');
const book = { title: 'JavaScript高级编程', author: 'Nicholas', price: 99 };
const request = store.add(book);
request.onsuccess = () => {
console.log('数据添加成功');
};
3. 查询数据
// 通过主键查询
const getRequest = store.get(1); // 获取主键为1的数据
getRequest.onsuccess = (event) => {
console.log('查询结果:', event.target.result);
};
// 通过索引查询
const index = store.index('titleIndex');
const indexRequest = index.getAll('JavaScript高级编程');
indexRequest.onsuccess = (event) => {
console.log('索引查询结果:', event.target.result);
};
4. 更新数据
const updateRequest = store.put({ id: 1, title: 'JS高级编程', price: 109 });
updateRequest.onsuccess = () => {
console.log('数据更新成功');
};
5. 删除数据
const deleteRequest = store.delete(1);
deleteRequest.onsuccess = () => {
console.log('数据删除成功');
};
四、高级操作
1. 使用游标遍历数据
const cursorRequest = store.openCursor();
cursorRequest.onsuccess = (event) => {
const cursor = event.target.result;
if (cursor) {
console.log('当前数据:', cursor.value);
cursor.continue(); // 继续遍历下一条
}
};
2. 批量操作与事务
const tx = db.transaction('books', 'readwrite');
const store = tx.objectStore('books');
// 批量添加数据
const books = [
{ title: '深入React', price: 89 },
{ title: 'Vue设计与实现', price: 79 }
];
books.forEach(book => store.add(book));
tx.oncomplete = () => {
console.log('批量操作完成');
};
3. 处理版本升级
// 版本从1升级到2时新增索引
request.onupgradeneeded = (event) => {
const db = event.target.result;
if (event.oldVersion < 2) {
const store = event.currentTarget.transaction.objectStore('books');
store.createIndex('priceIndex', 'price', { unique: false });
}
};
五、适用场景
- 离线应用:缓存用户数据,支持断网操作(如笔记应用、邮件客户端)。
- 大数据存储:存储用户生成的内容(如图片、文档的元数据)。
- 复杂查询:需要根据多个字段快速检索数据(如电商商品筛选)。
- 性能敏感操作:避免频繁请求后端,减少网络延迟。
六、性能优化建议
- 批量操作:减少事务数量,合并读写操作。
- 合理使用索引:仅为高频查询字段创建索引,避免过多索引降低写入性能。
- 游标分页:使用游标的
advance(N)分批加载数据,避免内存溢出。 - 关闭数据库:长时间不使用时主动关闭连接。
db.close();
七、常见问题
1. IndexedDB 与 LocalStorage 的区别?
| 特性 | IndexedDB | LocalStorage |
|---|---|---|
| 数据结构 | 结构化数据(对象、索引) | 简单键值对 |
| 容量 | GB级 | ~5MB |
| 查询能力 | 支持复杂查询、游标遍历 | 仅能通过键获取 |
| 异步性 | 异步API(不阻塞主线程) | 同步API(阻塞主线程) |
2. 如何处理浏览器隐私模式?
- 隐私模式(如无痕模式)下,IndexedDB 仍可用,但浏览器关闭后数据会被清除。
3. 如何备份/恢复数据?
- 导出:遍历数据生成 JSON 文件。
- 导入:解析 JSON 并批量写入。
八、安全注意事项
- 数据加密:敏感数据应在存储前加密(如使用
Web Crypto API)。 - 防 XSS 攻击:避免存储可执行代码,严格过滤输入。
- 权限控制:通过 Service Worker 拦截请求,实现更细粒度的访问控制。
IndexedDB 是构建现代 Web 应用的强大工具,合理使用可显著提升用户体验。建议结合 Dexie.js 等封装库简化操作,并参考 MDN IndexedDB 文档 深入学习。
网络安全常见考点
“网络安全”是前端面试中非常重要的一部分,特别是高级前端岗位,会关注你是否具备安全意识、常见攻击理解、防御能力和在工程实践中落地的经验。
一、常见网络安全威胁及考察点
1. XSS(跨站脚本攻击)
含义:攻击者在网页中注入恶意脚本,让浏览器执行。
考察点:
-
XSS 的类型(反射型、存储型、DOM 型)
-
攻击入口:表单、URL、DOM 插值、富文本
-
防范措施:
- 输出内容做转义(如 HTML Entity)
- 使用 CSP(内容安全策略)
- 禁止
innerHTML,使用安全 API(如textContent) - 使用第三方库(如 DOMPurify)做富文本清洗
- HttpOnly 防止读取 Cookie
2. CSRF(跨站请求伪造)
含义:诱导用户点击链接,对目标站点发起操作请求。
考察点:
-
原理:利用浏览器自动携带 Cookie 发起请求
-
攻击方式:诱导点击、图片加载等隐式提交
-
防范措施:
- 使用 Token 验证(CSRF Token)
- 检查
Referer或Origin头 - 请求接口限定为 POST,并校验内容
- SameSite Cookie 属性配置
3. 点击劫持(Clickjacking)
含义:攻击者在自己的页面上嵌套目标站,诱导用户点击。
考察点:
-
利用 iframe 蒙版 + 透明操作实现诱导点击
-
防范方式:
- 使用
X-Frame-Options: DENY/SAMEORIGIN - 使用
Content-Security-Policy: frame-ancestors控制嵌套来源 - 页面内 JS 检测
window.top !== window.self防 iframe 嵌套
- 使用
4. 中间人攻击(MITM)
含义:攻击者截取用户和服务端之间的数据通信内容。
考察点:
-
防御方式:
- 使用 HTTPS 加密数据传输
- 启用 HSTS,防止降级攻击
- 证书校验、防止证书伪造
5. 内容安全策略(CSP)
含义:通过响应头限制页面加载资源的来源,防止 XSS、数据劫持等。
考察点:
-
基本语法:
Content-Security-Policy: default-src 'self'; script-src 'self' cdn.com; -
如何配置 CSP 白名单
-
影响第三方脚本、图片等内容的加载
6. Cookie 安全
考察点:
-
Cookie 属性的安全配置:
Secure: 仅通过 HTTPS 传输HttpOnly: JS 无法访问(防止 XSS 窃取)SameSite:限制跨站携带行为
-
Cookie 泄露的常见途径
7. 前端加密相关
考察点:
- 前端是否能做加密?是否安全?
- 加密算法类型(Base64 ≠ 加密、MD5/SHA 加密不可逆)
- 前端加密常用于数据混淆,但不能代替后端校验
8. CORS(跨域资源共享)
考察点:
- 为什么浏览器会限制跨域?(同源策略)
Access-Control-Allow-Origin的配置方式withCredentials、预检请求、OPTIONS 请求- CORS 不能防 CSRF,需额外防护
9. 开放接口与 API 安全
考察点:
- 接口权限控制(不同用户是否能访问同一数据)
- 是否存在越权访问、参数篡改问题
- 接口是否存在 IDOR(不安全的直接对象引用)
10. 第三方脚本注入与依赖风险
考察点:
- 引入第三方脚本的安全性
Subresource Integrity (SRI):校验 CDN 脚本完整性- 软件供应链攻击(npm 依赖被污染)
- 如何使用 Snyk、npm audit 等工具进行依赖漏洞扫描
二、工程实践类考点
- 如何在项目中防止 XSS?
- 如何设计一个安全的文件上传功能?
- 如何在前端设计登录/鉴权流程?
- Token(如 JWT)的安全性、存储方式(localStorage vs Cookie)
- 你在实际项目中做过哪些安全加固处理?
三、常见面试问题示例
| 题目 | 涉及知识点 |
|---|---|
| 什么是 XSS?如何防范? | 输出转义、CSP、防 innerHTML |
| CSRF 如何攻击?如何防御? | Token、防跨域请求、SameSite |
| HTTPS 如何保障通信安全? | 加密、证书验证、MITM 防护 |
| 浏览器的同源策略限制了哪些行为? | 跨域限制、Cookie 访问 |
| JWT Token 应该放在哪?为什么? | localStorage/Cookie 的对比 |
| 如何防止 iframe 加载你的页面? | X-Frame-Options、CSP |
| 前端加密能防止数据泄露吗? | 加密 ≠ 安全,关键验证需后端完成 |
四、总结表格:前端网络安全考点总览
| 安全方向 | 关键点 |
|---|---|
| XSS | 输入校验、输出转义、CSP、富文本清洗 |
| CSRF | Token 验证、Referer 检查、SameSite Cookie |
| Clickjacking | X-Frame-Options、CSP 的 frame-ancestors |
| 中间人攻击 | 强制 HTTPS、证书验证、HSTS |
| Cookie 安全 | HttpOnly、Secure、SameSite |
| 加密与鉴权 | JWT 安全、Token 存储策略、数据加密混淆 |
| 第三方脚本 | SRI 校验、CDN 可信来源、依赖审计 |
| 文件上传 | 类型校验、后端验证、URL 白名单、重命名、隔离存储 |
| 接口权限 | 数据权限校验、操作权限、IDOR 检查 |
| CORS 与同源策略 | 请求限制、预检请求、响应头配置 |
关联面试题
前端的常规安全策略
- 定期请第三方机构做安全性测试,漏洞扫描
- 使用第三方开源库做上线前的安全测试,可以考虑融合到CI中
- code review 保证代码质量
- 默认项目中设置对应的 Header 请求头,如 X-XSS-Protection、 X-Content-Type-Options 、X-Frame-Options Header、Content-Security-Policy 等等
- 对第三方包和库做检测:NSP(Node Security Platform),Snyk
前端是否能做加密?是否安全?
前端可以做加密,但仅能提升数据传输 / 存储的安全性,无法做到绝对安全,核心结论如下:
-
能做的加密场景
- 传输加密:用 HTTPS (底层 TLS/SSL)加密前端与后端的通信,防止数据被窃听、篡改。
- 数据加密:前端对敏感数据(如用户输入的密码、个人信息)在发送前做对称加密(如 AES)或非对称加密(如 RSA),再传给后端。
- 代码混淆:对 JS/CSS 代码混淆压缩,增加逆向难度(如用 Terser、UglifyJS)。
-
安全局限性
- 前端代码运行在用户浏览器中,密钥、加密逻辑可能被扒取(比如通过开发者工具调试、反混淆代码)。
- 加密只是 “增加攻击成本”,无法阻止有经验的攻击者逆向破解。
- 核心敏感逻辑(如验签、权限校验)必须放在后端,前端加密仅作为辅助手段。
简单说:前端加密是 “防君子不防小人”,需结合后端校验才能保障安全。
你在实际项目中做过哪些安全加固处理?
在前端项目中,安全加固主要围绕 数据传输、代码防护、XSS/CSRF 防御、依赖安全 四个核心方向,以下是实际项目中常用的加固手段:
-
传输层安全
- 全站强制 HTTPS,配置
Strict-Transport-Security(HSTS)响应头,禁止 HTTP 降级访问,防止中间人攻击。 - 敏感数据(如用户密码、token)在前端用 AES 对称加密 后再传输,后端解密校验,避免明文传输泄露。
- 全站强制 HTTPS,配置
-
XSS 攻击防御
- 输入输出校验:用户输入内容(如评论、表单)用
DOMPurify过滤恶意标签;渲染动态内容优先用v-text(Vue2)而非v-html。 - 配置
Content-Security-Policy(CSP)响应头,限制脚本加载源(如只允许同源和可信 CDN),阻止内联脚本和未授权插件执行。 - 设置
HttpOnly和SameSite=Strict属性,防止 Cookie 被 JS 窃取和 CSRF 攻击。
- 输入输出校验:用户输入内容(如评论、表单)用
-
代码与依赖防护
- 生产环境代码 混淆压缩:用 Webpack + Terser 移除注释、调试代码,变量名随机化,增加逆向难度。
- 定期扫描依赖漏洞:用
npm audit或Snyk检测第三方包(如lodash、axios)的高危漏洞,及时升级修复。 - 禁用
eval()、Function()等危险函数,防止代码注入。
-
接口与权限防护
- 接口请求加 签名校验:前端请求参数拼接时间戳、nonce 随机串和密钥生成签名,后端验证签名有效性,防止参数篡改。
- 实现 请求限流:前端对高频接口(如登录、提交)做防抖节流,配合后端接口限流,防止恶意刷接口。
- token 安全管理:用
localStorage存储普通 token,敏感操作(如支付)用短时sessionStorage,且 token 过期时间不宜过长。
-
其他加固手段
- 隐藏前端版本信息:移除
X-Powered-By等响应头,避免暴露技术栈(如 Vue2、Webpack)。 - 配置
Referrer-Policy: same-origin,防止敏感页面的来源信息泄露。
- 隐藏前端版本信息:移除
介绍SSL和TLS ⭐️ 重点
SSL(Secure Sockets Layer)和 TLS(Transport Layer Security) 是用于保护网络通信安全的加密协议,广泛应用于 HTTPS、电子邮件、VPN 等场景。
SSL:(
安全套 接层)。 SSL通过互相认证、使用数字签名确保完整性、使用加密确保私密性,以实现客户端和服务器之间的安全通讯。该协议由两层组成:SSL记录协议和SSL握手协议。TLS:(
传输层 安全协议),用于两个应用程序之间提供保密性和数据完整性。该协议由两层组成:TLS记录协议和TLS握手协议。
- SSL 已死,TLS 当立:所有场景应使用 TLS 1.2 或 1.3。
- TLS 1.3 通过 1-RTT 握手和高效加密算法,兼顾速度和安全性。
基本定义
-
SSL(安全套接层)
- 已淘汰:所有 SSL 版本(SSL 1.0/2.0/3.0)均存在严重漏洞,不再被使用。
-
TLS(传输层安全协议)
- 是 SSL 的继任者,由 IETF 标准化(基于 SSL 3.0 改进)。
- 当前版本:TLS 1.2(主流)、TLS 1.3(最新且最安全)。
典型应用场景
- HTTPS
- 电子邮件加密
- VPN 通信
为什么说“SSL”实际是指 TLS?
- 历史习惯:
由于 SSL 名称更早普及,许多场景仍沿用“SSL”代指 TLS(如“SSL 证书”实际是 TLS 证书)。 - 商业术语:证书颁发机构(CA)和市场宣传中常使用“SSL”一词。
HTTPS怎么建立安全通道 / HTTPS工作原理 / SSL/TLS 握手过程 ⭐️⭐️⭐️ 热点重点
HTTPS 通过 TLS/SSL 协议 建立安全通道,核心流程分为 TLS 握手 和 加密通信 两个阶段。以下是详细步骤:
一、TLS 握手阶段
(以 TLS 1.3 为例,优化了握手步骤,仅需 1-RTT)
1. 客户端发起请求(Client Hello)
- 发送内容:
- 支持的 TLS 版本(如 TLS 1.3)。
- 客户端生成的随机数(Client Random)。
- 支持的加密套件列表(如
TLS_AES_128_GCM_SHA256)。 支持的密钥交换算法(如 ECDHE)。
2. 服务器响应(Server Hello)
服务器收到客户端的请求后,根据客户端提供的信息,选择一种双方都支持的 TLS版本和加密套件。然后,服务器将自己的数字证书发送给客户端。
- 发送内容:
- 选择的 TLS 版本和加密套件。
- 服务器生成的随机数(Server Random)。
- 数字证书(含公钥、证书颁发机构(CA)信息、服务器的域名、到期信息)。
- 服务器密钥交换参数(如椭圆曲线参数和服务端公钥)。
3. 客户端验证证书
- 验证步骤:
- 检查证书是否由受信任的证书颁发机构 签发(如 DigiCert、Let's Encrypt)。
客户端通常内置了一些受信任的 CA 的根证书,通过验证服务器证书的签名是否与这些根证书匹配来确定证书的可信度。 - 确保证书未过期且域名匹配(防止钓鱼网站)。
如果证书已过期或尚未生效,客户端将拒绝接受该证书。检查证书中的域名是否与当前访问的域名一致。这是为了防止中间人攻击,确保客户端连接到正确的服务器 - 验证证书链完整性(根证书 → 中间证书 → 服务器证书)。
- 检查证书是否由受信任的证书颁发机构 签发(如 DigiCert、Let's Encrypt)。
4.生成会话密钥
- 客户端生成一个随机的会话密钥,这是一个用于对称加密的密钥。
- 然后,客户端使用服务器的公钥对会话密钥进行加密,将加密后的会话密钥发送给服务器。
5.解密会话密钥
- 服务器使用自己的私钥解密客户端发送的加密会话密钥,得到原始的会话密钥。
- 此时,
客户端和服务器都拥有了相同的会话密钥。
6.安全通信
双方使用会话密钥对后续的通信数据进行加密和解密。在数据传输过程中,数据会被分成多个数据包,每个数据包都会使用会话密钥进行加密。同时,为了保证数据的完整性,还会使用消息认证码(MAC)对数据进行校验。客户端和服务器在收到数据包后,会使用会话密钥进行解密,并验证 MAC 以确保数据没有被篡改。
通过以上步骤,HTTPS 建立了一个安全的通信通道,保证了数据在传输过程中的保密性、完整性和身份认证,有效防止了数据被窃取、篡改和中间人攻击等安全威胁。
二、加密通信阶段
握手完成后,使用 对称加密 传输数据:
- 加密算法:如 AES-128-GCM(高效且安全)。
- 数据完整性:通过 AEAD(如 GCM 模式)同时实现加密和完整性校验。
三、关键组件解析
1. 非对称加密 vs 对称加密
| 类型 | 用途 |
|---|---|
| 非对称加密 | 握手阶段交换密钥 |
| 对称加密 | 加密实际通信数据 |
2. 前向保密(Forward Secrecy)
- 原理:每次会话使用临时密钥(如 ECDHE),即使长期私钥泄露,历史通信仍安全。
- 实现:TLS 1.3 强制启用前向保密。
四、安全通道的保障
- 防窃听:
对称加密确保数据无法被解密。 - 防篡改:哈希算法,用于生成消息认证码(MAC) ,对传输的数据进行完整性校验
- 防伪装:数字证书验证阻止中间人攻击。
总结
HTTPS 通过 TLS 握手协商密钥 + 对称加密传输数据 建立安全通道,解决了 HTTP 的明文传输风险。现代 TLS 1.3 进一步优化了安全性与性能,是保障 Web 通信安全的基石。
HTTPS是如何实现加密
SSL证书的作用就是实现网站HTTPS化,使网站可信,防窃听、防篡改、防伪装。
如果网站涉及输入用户名密码等敏感信息的那么https的强大作用就会发挥的淋漓尽致,加密数据并进行身份验证,免费的SSL证书是没有身份验证这项功能的,因此,免费SSL证书是功能不完整的,使用需慎重。
HTTPS 通过 SSL/TLS 协议实现加密,主要利用了对称加密、非对称加密和哈希算法等技术,具体过程如下:
-
对称加密:在 HTTPS 连接建立后,客户端和服务器会协商
生成一个共享的会话密钥。这个密钥用于对后续传输的数据进行加密和解密,由于加密和解密使用相同的密钥,所以对称加密速度快,适合大量数据的加密。 -
非对称加密:在 HTTPS 握手阶段,服务器将自己的公钥通过数字证书发送给客户端,客户端使用服务器的公钥对随机生成的会话密钥进行加密,并发送给服务器。服务器使用自己的私钥解密得到会话密钥。非对称加密用于安全地传输会话密钥,保证了密钥在传输过程中的安全性。 公钥和私钥都可以用来加密解密,但公钥加密后只能用私钥解密,反过来,私钥加密后也只能用公钥解密
-
哈希算法:用于生成消息认证码(MAC),对传输的数据进行完整性校验。在数据发送端,根据数据和会话密钥使用哈希算法生成 MAC 值,随数据一起发送。接收端收到数据后,使用相同的算法和密钥重新计算 MAC 值,并与接收到的 MAC 值进行比较,若一致则说明数据完整未被篡改。
HTTPS 中数字证书的用途
-
身份认证:证书用于证明服务器的身份。客户端通过验证证书,可以确认服务器是否是其声称的身份,是否被信任的证书颁发机构(CA)所认可,从而防止客户端连
接到假冒的服务器,避免中间人攻击。 -
传递公钥:证书中包含服务器的公钥,客户端可以从证书中提取公钥,用于加密会话密钥等信息,确保与服务器之间的通信安全。客户端只有在验证证书合法后,才会与服务器进行加密通信。
用证书部署 HTTPS 的步骤
- 获取证书
- 从 CA 购买或申请免费证书(如 Let’s Encrypt)。
- 安装证书
- 在服务器配置证书文件(
.crt)和私钥(.key)。
- 在服务器配置证书文件(
- 配置服务器
- 强制 HTTP 跳转到 HTTPS,启用 HSTS(严格传输安全)。
客户端如何验证证书的合法性
- 哈希计算:服务端使用与 CA 签名时相同的哈希算法,对数字证书中进行哈希计算,得到一个哈希值 H1 。
- 解密签名:客户端使用 CA 的公钥对证书中的数字签名进行解密,得到另一个哈希值 H2 。
- 结果比对:将 H1 和 H2 进行比较,若二者相同,说明证书在签发后未被篡改,签名验证通过;若不同,则表明证书可能已被篡改或不合法。
讲一下数字签名的原理 ⭐️⭐
数字签名是一种用于验证文档真实性和完整性的技术,其原理主要基于公钥加密技术和哈希算法,具体过程如下:
- 生成密钥对:首先客户端使用密钥生成算法生成一对密钥,即公钥和私钥。私钥(自己藏好) 和公钥(公开给接收方) ;
- 计算哈希值:对要签名的数据使用哈希函数进行计算,得到哈希值。
哈希函数具有单向性和唯一性,即不同的消息很难产生相同的哈希值,而且从哈希值很难反推出原始消息。 - 签名生成:客户端使用自己的私钥对计算出的哈希值进行加密,得到数字签名。
这个过程实际上是使用私钥对哈希值进行了一次变换,只有拥有相应私钥的用户才能完成这个操作。 - 消息传输:将
原始消息和数字签名一起发送给接收方。 - 签名验证:接收方收到消息和数字签名后,首先使用与发送方相同的哈希函数对收到的原始消息进行计算,得到一个新的哈希值。然后,接收方使用发送方的公钥对数字签名进行解密,得到发送方计算的哈希值。最后,将这两个哈希值进行比较,如果它们相等,说明消息在传输过程中没有被篡改,并且确实是由拥有相应私钥的发送方发送的,即验证了消息的真实性和完整性;如果不相等,则说明消息可能被篡改过,或者不是由合法的发送方发送的。
数字签名技术通过结合公钥加密和哈希函数的特性,实现了对数字消息的身份认证、完整性验证和不可否认性,在信息安全领域有着广泛的应用,如电子商务、电子政务、电子邮件等场景中,确保了数据的安全和可信。
前端如何实现 token 加密 ⭐️
在前端实现 token 加密,通常是为了增强数据在传输过程中的安全性,避免 token 被恶意窃取和篡改。
使用对称加密算法(如 AES)
AES(高级加密标准)是一种广泛使用的对称加密算法,前端可以借助 crypto-js 库来实现 AES 加密。
步骤 1:安装依赖
通过 npm 安装加密库:
npm install crypto-js
步骤 2:AES 加密 / 解密工具函数
AES 加密需要密钥(key) 和模式(推荐CBC模式,需额外偏移量iv),示例代码如下:
import CryptoJS from 'crypto-js';
// 密钥和偏移量(需与后端一致,建议后端生成后前端存储,长度:key=16位,iv=16位)
const AES_KEY = CryptoJS.enc.Utf8.parse('1234567890abcdef'); // 16位密钥
const AES_IV = CryptoJS.enc.Utf8.parse('abcdef1234567890'); // 16位偏移量
/**
* AES加密(CBC模式)
* @param {string} token - 需要加密的token
* @returns {string} 加密后的base64字符串
*/
export function encryptToken(token) {
// 将token转为UTF8编码的字节数组
const srcs = CryptoJS.enc.Utf8.parse(token);
// 加密(mode=CBC,padding=Pkcs7)
const encrypted = CryptoJS.AES.encrypt(srcs, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回base64格式的密文
return encrypted.ciphertext.toString(CryptoJS.enc.Base64);
}
/**
* AES解密(对应加密方法)
* @param {string} encryptedToken - 加密后的token
* @returns {string} 解密后的原始token
*/
export function decryptToken(encryptedToken) {
// 将base64密文转为字节数组
const encryptedHexStr = CryptoJS.enc.Base64.parse(encryptedToken);
const srcs = CryptoJS.enc.Base64.stringify(encryptedHexStr);
// 解密
const decrypted = CryptoJS.AES.decrypt(srcs, AES_KEY, {
iv: AES_IV,
mode: CryptoJS.mode.CBC,
padding: CryptoJS.pad.Pkcs7
});
// 返回UTF8格式的明文
return decrypted.toString(CryptoJS.enc.Utf8);
}
步骤 3:使用示例
// 原始token
const rawToken = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...';
// 加密
const encrypted = encryptToken(rawToken);
console.log('加密后:', encrypted); // 类似:"xYjK3pQ...(长字符串)"
// 解密(通常在需要使用token时解密,如请求头携带前)
const decrypted = decryptToken(encrypted);
console.log('解密后:', decrypted); // 输出原始token
关键注意事项
- 密钥安全性:前端密钥(key/iv)无法完全隐藏(可被反编译获取),因此不能仅依赖前端加密,后端必须对 token 本身进行验证(如 JWT 的签名校验)。
- 模式选择:推荐
CBC模式(需 iv),避免使用 ECB 模式(不安全,相同明文加密结果相同)。 - 前后端一致性:加密模式、密钥、偏移量、填充方式(如 Pkcs7)必须与后端完全一致,否则解密会失败。
- 适用场景:主要用于防止 token 在前端存储(如 localStorage)时被明文窃取,增加攻击者破解成本。
如果是纯前端环境(无构建工具),可直接引入crypto-js的 CDN 脚本使用,用法类似。
使用 JWT(JSON Web Token)
JWT 本身可以通过设置密钥进行签名,以确保 token 的完整性和真实性。前端通常使用 jsrsasign 库来处理 JWT。
安装依赖
npm install jsrsasign
示例代码
<body>
<script>
// 生成 JWT Token
function generateJwtToken(userData, secretKey) {
const header = {
alg: "HS256",
typ: "JWT"
};
const payload = {
...userData,
exp: KJUR.jws.IntDate.get("now + 1h")
};
const sHeader = JSON.stringify(header);
const sPayload = JSON.stringify(payload);
const sJWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, secretKey);
return sJWT;
}
// 验证 JWT Token
function verifyJwtToken(token, secretKey) {
return KJUR.jws.JWS.verifyJWT(token, secretKey, { alg: ["HS256"] });
}
// 示例使用
const userData = {
userId: 1,
username: "exampleUser"
};
const secretKey = "your_secret_key";
const jwtToken = generateJwtToken(userData, secretKey);
const isVerified = verifyJwtToken(jwtToken, secretKey);
console.log("Generated JWT Token:", jwtToken);
console.log("Is Token Verified:", isVerified);
</script>
</body>
</html>
这段代码定义了 generateJwtToken 和 verifyJwtToken 函数,分别用于生成和验证 JWT token。使用时,需提供用户数据和密钥。
注意事项
- 密钥管理:对称加密的密钥和 JWT 的签名密钥都要妥善保管,避免泄露。通常,这些密钥应该由后端生成和管理,前端通过安全的方式获取。
- 跨域问题:如果涉及跨域请求,要确保加密和解密的环境一致,避免因环境差异导致加密结果不一致。
- 兼容性:在使用加密库时,要考虑不同浏览器和设备的兼容性,确保加密功能在各种环境下都能正常工作。
介绍下前端加密的常见场景和方法 ⭐️
前端加密的常见方法 实操
1. 密码哈希处理
- 场景:
用户注册/登录时密码传输。 - 方法:
- 前端对密码加盐(Salt)后哈希(如 SHA-256),再传输哈希值。
- 示例(使用 Web Crypto API):
async function hashPassword(password, salt) { const encoder = new TextEncoder(); const data = encoder.encode(password + salt); const hashBuffer = await crypto.subtle.digest('SHA-256', data); return Array.from(new Uint8Array(hashBuffer)).map(b => b.toString(16).padStart(2, '0')).join(''); }
- 注意:哈希不能替代 HTTPS,且需服务端二次哈希+盐存储。
2. 对称加密(AES)
- 场景:
加密传输或存储的敏感数据(如用户手机号)。 - 方法:
- 使用 AES 算法,密钥由服务端动态下发或基于用户密码派生。
- 示例(使用
crypto-js):import AES from 'crypto-js/aes'; import enc from 'crypto-js/enc-utf8'; // 加密 const ciphertext = AES.encrypt('敏感数据', '密钥').toString(); // 解密 const bytes = AES.decrypt(ciphertext, '密钥'); const originalText = bytes.toString(enc);
- 注意:避免硬编码密钥,可通过 HTTPS 动态获取或使用密钥派生算法(PBKDF2)。
3. 非对称加密(RSA)
- 场景:
加密对称密钥或数字签名验证 - 方法:
- 前端使用公钥加密数据,服务端用私钥解密。
- 示例(使用 Web Crypto API):
async function rsaEncrypt(publicKey, data) { const encoder = new TextEncoder(); const encrypted = await window.crypto.subtle.encrypt( { name: 'RSA-OAEP' }, publicKey, encoder.encode(data) ); return btoa(String.fromCharCode(...new Uint8Array(encrypted))); }
- 注意:非对称加密性能较低,通常仅用于加密密钥或小数据。
4. JWT(JSON Web Token)
- 场景:安全传输用户身份信息(如登录态)。
- 方法:
- 服务端生成签名后的 Token,前端存储并在请求时携带。
- 结构:
Header.Payload.Signature,签名防止篡改。
- 注意:JWT 默认不加密(仅 Base64 编码),敏感信息需配合加密算法。
5. 本地存储加密
- 场景:加密
localStorage中的敏感信息(如临时 Token)。 - 方法:
- 使用 AES 或库(如
crypto-js)加密后存储。 - 示例:
const encryptedData = AES.encrypt(JSON.stringify(data), 'key').toString(); localStorage.setItem('encryptedData', encryptedData);
- 使用 AES 或库(如
- 注意:密钥需动态获取(如从服务端),避免硬编码
6. 客户端数据签名
- 场景:防止 API 请求参数被篡改(如支付金额、订单号)。
- 方法:
- 前端对参数排序后拼接,加盐哈希生成签名。
- 示例:
function signParams(params, salt) { const sortedStr = Object.keys(params).sort().map(k => `${k}=${params[k]}`).join('&'); return sha256(sortedStr + salt); } // 请求时携带参数和签名 fetch('/api/pay', { method: 'POST', body: JSON.stringify({ ...params, sign: signParams(params, 'salt') }) });
7. 混淆与代码保护
- 场景:防止前端加密逻辑被逆向分析。
- 方法:
- 使用代码混淆工具(如 JavaScript Obfuscator)。
- 将核心加密逻辑部署在 Web Worker 或 WASM 中。
安全注意事项
- 密钥管理:
- 永远不要在前端硬编码密钥,应通过 HTTPS 动态获取。
- 使用临时密钥或基于用户凭证派生密钥(如 PBKDF2)。
- 算法选择:
- 避免使用 MD5、SHA-1 等弱算法,优先选择 AES-GCM、SHA-256、RSA-OAEP。
- 防重放攻击:
- 在加密数据中加入时间戳或随机数(Nonce),服务端校验有效性。
- XSS 防御:
- 加密不能替代 XSS 防护,需严格过滤输入输出,设置 CSP 策略。
介绍下 HTTPS 中间人攻击 ⭐️⭐️ 重点
HTTPS 中间人攻击(Man-in-the-Middle Attack, MITM)是指攻击者在客户端与服务器之间 拦截并篡改加密通信 的一种攻击方式。尽管 HTTPS 通过 TLS/SSL 协议提供了加密和身份验证,但在某些特定条件下,攻击者仍可能绕过这些保护机制。
一、中间人攻击的原理
-
核心目标
- 窃听数据:获取加密通信的信息(如
账号密码、支付信息)。 - 篡改数据:修改请求或响应内容(如植入恶意代码)。
- 伪装身份:
冒充合法服务器或客户端进行欺诈。
- 窃听数据:获取加密通信的信息(如
-
攻击前提
- 攻击者能够控制网络流量(如通过 ARP 欺骗、DNS 劫持、恶意 Wi-Fi 热点)。
- 客户端或服务器存在安全漏洞(
如信任非法证书、忽略证书警告)。
二、HTTPS 中间人攻击的实现方式
1. 伪造证书攻击
-
步骤:
- 攻击者伪造目标服务器的证书(如自签名证书或非法 CA 签发的证书)。
- 诱导用户信任攻击者的根证书(如通过钓鱼邮件或恶意软件安装)。
- 客户端误认为攻击者的证书合法,建立加密连接,攻击者解密并转发流量。
-
典型案例:
- 公共 Wi-Fi 中,攻击者劫持流量并提示用户安装“安全证书”。
- 恶意软件在用户设备中预装攻击者 CA 证书。
2. SSL/TLS 协议降级攻击
-
原理:
- 强制客户端与服务端使用低版本 TLS(如 SSL 3.0)或弱加密套件(如 RC4),利用协议漏洞解密通信。
- 例如:POODLE 攻击(Padding Oracle On Downgraded Legacy Encryption)。
-
防御:
- 服务器禁用 SSL 3.0 和旧版 TLS,强制使用 TLS 1.2/1.3。
3. SSL 剥离(SSL Stripping)
-
原理:
- 攻击者将客户端的 HTTPS 请求降级为 HTTP,截取明文数据后,再以 HTTPS 与服务器通信。
- 依赖用户未强制使用 HTTPS(如手动输入
http://)。
-
防御:
- 启用 HSTS(HTTP Strict Transport Security),
强制浏览器仅通过 HTTPS 连接。
- 启用 HSTS(HTTP Strict Transport Security),
三、攻击过程示例(以伪造证书为例)
- 流量拦截:攻击者通过 ARP 欺骗将用户流量导向自己。
- 伪造证书:攻击者生成一个与目标网站域名匹配的伪造证书。
- 建立连接:
- 客户端发起 HTTPS 请求,攻击者冒充服务器返回伪造证书。
- 若用户忽略浏览器证书警告,连接建立,攻击者解密数据。
- 数据中转:
- 攻击者将解密后的数据转发给真实服务器,并返回响应给用户。
- 用户全程无感知,但所有通信已被窃听或篡改。
四、防御中间人攻击的关键措施
1. 严格证书验证
- 客户端行为:
- 检查证书是否由受信任的 CA 签发(如 Let's Encrypt、DigiCert)。
- 确保证书域名匹配、未过期且未被吊销(启用 OCSP Stapling)。
- 服务端配置:
- 使用强加密套件(
如 TLS\_AES\_128\_GCM\_SHA256)。 - 启用 HSTS 头,强制 HTTPS 并禁止降级:
Strict-Transport-Security: max-age=31536000; includeSubDomains; preload
- 使用强加密套件(
五、实际攻击案例
- Superfish 事件(2015 年)
- 联想预装软件 Superfish 在用户设备中注入自签名根证书,导致 HTTPS 流量可被解密。
- 恶意公共 Wi-Fi
- 攻击者在咖啡馆部署伪造 Wi-Fi,诱导用户连接后实施 SSL 剥离或证书欺骗。
六、总结
HTTPS 中间人攻击的核心在于破坏 TLS 的信任链或利用协议漏洞。防御需多管齐下:
- 服务端:正确配置 TLS,启用 HSTS 和证书透明化。
- 客户端:严格验证证书,避免信任非法来源。
- 用户:提高安全意识,警惕异常网络环境。
尽管 HTTPS 显著提升了通信安全,但唯有技术、配置与用户行为的结合,才能有效抵御中间人攻击。
说一说JWT,以及适用场景和优缺点
什么是 JWT
JWT 即 JSON Web Token,是一种开放标准(RFC 7519),用于在各方之间安全地传输声明。本质上它是一个紧凑且自包含的 JSON 对象,以字符串形式存在,通常由三部分组成,用点(.)分隔:
JWT 由三部分组成,以点(
.)分隔:
Header.Payload.Signature
- Header(头部) :包含两部分信息,令牌的类型(通常是 JWT)和使用的签名算法,如 HMAC SHA256 或 RSA。例如:
{ "typ": "JWT"
"alg": "HS256",
}
之后会对这个 JSON 对象进行 Base64Url 编码,形成 JWT 的第一部分。
-
Payload(负载) :包含声明(Claims),声明是关于实体(通常是用户)和其他数据的声明。声明分为三种类型:
- 注册声明:如 iss(发行人)、sub(主题)、aud(受众)等,这些是由 JWT 标准定义的。
- 公开声明:由各方自由定义。
- 私有声明:在同意使用的一方之间定义。
示例如下:
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
同样会对其进行 Base64Url 编码,得到 JWT 的第二部分。
- Signature(签名) :为了创建签名部分,需要使用编码后的 Header、编码后的 Payload、一个密钥(secret)和 Header 中指定的签名算法。例如使用 HMAC SHA256 算法,签名将按以下方式创建:
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload),secret)
签名用于验证消息在传输过程中没有被更改,并且在使用私钥签名的情况下,还可以验证 JWT 的发送者的身份。
适用场景
- 身份验证:在单页应用(SPA)或移动应用中,用户登录后,服务器可以生成一个 JWT 并返回给客户端。客户端在后续的请求中携带这个 JWT,服务器通过验证 JWT 来确认用户的身份,
而无需在服务器端存储会话信息,便于实现无状态的认证机制。 - 信息交换:JWT 可以安全地在各方之间传输信息。由于 JWT 可以被签名,接收方可以验证 JWT 的完整性和真实性,确保信息在传输过程中没有被篡改。例如,在
不同服务之间传递用户信息时可以使用 JWT。
优点
- 无状态:服务器不需要存储会话信息,所有的用户信息都包含在 JWT 中,这使得服务器更容易扩展,因为它不需要在多个服务器之间共享会话数据。
- 跨域支持:由于 JWT 通常是通过 HTTP 请求头或 URL 参数传递的,因此它可以很容易地跨域使用,适合在不同的域名或服务之间进行身份验证和信息交换。
- 自包含:载荷可直接包含用户信息,减少数据库查询。
缺点
- 无法主动撤销:令牌在过期前有效,需借助黑名单或短有效期降低风险。
- 载荷暴露:Base64 可解码,敏感数据需加密处理。
- 体积较大:
比 Session ID 更长,可能增加网络开销。
JWT 的工作流程
-
用户登录:
客户端提交凭证(如用户名密码),服务器验证通过后生成 JWT 并返回。 -
客户端存储:
客户端将 JWT 存储在localStorage或Cookie中。 -
携带令牌请求:
客户端在后续请求的Authorization头中附加 JWT:Authorization: Bearer <JWT> -
服务端验证:
服务器验证签名有效性、过期时间及声明,通过后处理请求。
具体逻辑:
JWT 中通过签名机制来证明 token 未被篡改,其流程如下:
-
生成签名:在服务器端生成 JWT 时,会使用一个只有服务器知道的密钥(secret key),结合指定的签名算法(如 HS256、RS256 等)对 JWT 的头部(Header)和载荷(Payload)进行签名计算。以 HS256 算法为例,计算方式是使用密钥对头部和载荷的字符串进行哈希运算,生成一个固定长度的哈希值,这个哈希值就是签名。
-
组装 JWT:将头部、载荷和签名用
.连接起来,形成完整的 JWT 字符串,然后发送给客户端。 -
验证签名:客户端收到 JWT 后,将其发送给服务器进行验证。服务器接收到 JWT 后,会按照同样的算法和密钥,对 JWT 的头部和载荷进行重新签名计算。
-
对比签名:服务器将重新计算得到的签名与 JWT 中携带的签名进行对比,如果两者完全一致,就可以证明 JWT 在传输过程中没有被篡改。因为即使 JWT 的头部或载荷被篡改,重新计算出的签名也会与原始签名不同,从而能够检测到篡改行为。
整个过程中,密钥的保密性至关重要,只要密钥不被泄露,攻击者就无法生成有效的签名来篡改 JWT。