[{"data":1,"prerenderedAt":8692},["ShallowReactive",2],{"algolia":3},[4,1031,1963,3034],{"id":5,"title":6,"articleId":7,"articleThumb":8,"body":9,"categoryId":691,"categoryInfo":991,"createDate":994,"description":995,"excerpt":996,"extension":1001,"isHidden":1002,"isHot":1003,"isRecommend":1003,"isTop":1003,"meta":1004,"navigation":1003,"path":1005,"rawbody":1006,"readingTime":1007,"seo":1008,"sitemap":1009,"stem":1022,"tagIds":1023,"tagInfo":1024,"updateDate":1028,"wordCount":1029,"__hash__":1030},"blog\u002Fblog\u002F1.Algolia DocSearch快速开启网站全文检索功能.md","Algolia DocSearch快速开启网站全文检索功能","algolia-docSearch","https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327202906034.webp",{"type":10,"value":11,"toc":971},"minimark",[12,16,21,60,64,97,101,111,122,127,185,189,215,219,222,231,235,238,252,256,259,394,398,402,416,423,444,450,454,472,498,504,510,528,533,539,549,554,557,565,571,576,586,604,607,617,622,632,652,656,671,675,738,742,764,912,916,923,962,967],[13,14,15],"p",{},"Algolia 的服务支持多种语言支持 Ruby、Rails、Python、PHP、JS、Java、Android、Objective-C 等多种语言，具备关键字输入智能容错功能，并提供有搜索排名配置，并具有实时快速的特点(Algolia 在全球 12 个数据中心均有部署)。Algolia 还为移动设备提供了离线搜索引擎，其 C++ SDK 可以嵌入到应用服务器端，这样即便没有网络连接应用也能提供搜索功能。",[17,18,20],"h2",{"id":19},"_1-相关导航链接","1. 相关导航链接",[22,23,24,34,46,53],"card-group",{},[25,26,31],"card",{"icon":27,"target":28,"title":29,"to":30},"i-simple-icons-github","_blank","DocSearch免费服务申请页面","https:\u002F\u002Fdocsearch.algolia.com\u002Fapply\u002F",[13,32,33],{},"点击跳转DocSearch免费搜索服务申请页面，申请通过后，可快速集成DocSearch到项目中。",[25,35,38],{"icon":27,"target":28,"title":36,"to":37},"Algolia管理后台页面","https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Fdashboard",[13,39,40,41,45],{},"点击跳转Algolia管理后台，查看应用",[42,43,44],"code",{},"keys","，管理Algolia应用，索引和记录数据。",[25,47,50],{"icon":27,"target":28,"title":48,"to":49},"algolia\u002Fdocsearch 源码仓库","https:\u002F\u002Fgithub.com\u002Falgolia\u002Fdocsearch",[13,51,52],{},"点击跳转docsearch源码仓库，查看不同框架中docsearch的集成步骤和使用方式。",[25,54,57],{"icon":27,"target":28,"title":55,"to":56},"Algolia爬虫后台页面","https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Fcrawler\u002Fcrawlers",[13,58,59],{},"点击跳转Algolia爬虫管理后台，查看爬虫程序执行结果，手动重新触发爬虫任务或者自定义爬虫程序。",[17,61,63],{"id":62},"_2-algolia功能和特点","2. Algolia功能和特点",[65,66,67,75,81,87],"ol",{},[68,69,70,74],"li",{},[71,72,73],"strong",{},"托管式全文搜索："," Algolia 提供完整的搜索服务后端，无需部署，API即用。开发者无需自行构建搜索引擎架构，只需调用API即可实现实时搜索、模糊匹配、多语言支持等功能，轻量级集成。API响应速度快，低延迟、精准度高、有免费计划支持以及易于集成。",[68,76,77,80],{},[71,78,79],{},"性能与扩展性‌："," 支持每秒处理数千次查询，延迟低至20毫秒；‌‌可扩展至数十亿条数据，适用于电商、内容平台等高并发场景;",[68,82,83,86],{},[71,84,85],{},"‌开发者友好性‌："," 提供JavaScript、Python、Java等主流语言的SDK; 支持与Hexo、Hugo、Apifox、VuePress、VitePress等开发工具无缝集成，配置时间可缩短至数分钟；",[68,88,89,92,93,96],{},[71,90,91],{},"使用成本：","  免费版支持1万条索引和1万次\u002F月搜索请求，个人用户基本无需付费；技术文档与开源项目‌，通过申请免费",[42,94,95],{},"DocSearch","计划，自动化索引生成和搜索服务部署，显著提升文档用户体验；‌‌",[17,98,100],{"id":99},"_3-algolia-docsearch","3. Algolia DocSearch",[13,102,103,110],{},[104,105,109],"a",{"href":106,"rel":107,"target":28},"https:\u002F\u002Fdocsearch.algolia.com\u002Fdocs\u002Fwhat-is-docsearch",[108],"nofollow","Algolia DocSearch"," 是Algolia提供的免费的自动化全文搜索服务，旨在帮助开发者为他们的文档网站提供高效、精确的搜索体验。它通过智能爬虫抓取和索引在线文档，定时爬取指定网站，自动构建搜索索引，并结合Algolia的高性能搜索技术，提供即时的、上下文相关的搜索结果‌。",[13,112,113,114,117,118,121],{},"对于开发者而言，不需要繁琐的配置，只需要申请",[42,115,109],{"color":116},"primary","服务的免费计划后，用相关的 API 直接调用服务就行了。目前前端框架都有相对成熟的",[42,119,120],{"color":116},"Algolia DocSearch API","快速集成的第三方库，使用非常简单。对于使用者，只需要在网站检索框输入关键字，即可快速的搜索相应结果。",[123,124,126],"h3",{"id":125},"_31-功能和特点","3.1. 功能和特点",[128,129,130,137,143,149,155,161,167,173,179],"ul",{},[68,131,132,133,136],{},"‌",[71,134,135],{},"智能爬虫‌："," Algolia DocSearch使用定制的爬虫技术，能够深入到网站结构中，有效地抓取和提取文档信息。用户可以通过配置文件指导爬虫的工作，包括要抓取的页面、字段和过滤规则‌.",[68,138,132,139,142],{},[71,140,141],{},"数据索引‌："," 爬虫抓取的信息会被上传至Algolia的搜索索引，这是一个经过优化的数据存储，适合快速查询。Algolia支持丰富的搜索功能，如拼写纠错、部分匹配和布尔运算等‌.",[68,144,132,145,148],{},[71,146,147],{},"自定义搜索界面‌："," Algolia提供了JavaScript SDK，允许用户在自己的网站上轻松嵌入搜索框，并自定义搜索结果显示样式和交互体验‌.",[68,150,132,151,154],{},[71,152,153],{},"持续更新‌："," 配合 GitCode 上的配置仓库，每次更新文档时，DocSearch可以自动或按需重新索引，确保搜索结果始终与最新内容同步‌.",[68,156,157,160],{},[71,158,159],{},"即时性‌："," Algolia搜索引擎设计用于提供毫秒级的响应速度‌.",[68,162,163,166],{},[71,164,165],{},"准确性‌："," 通过深度学习和自然语言处理技术，提高搜索结果的相关度‌.",[68,168,169,172],{},[71,170,171],{},"可定制化‌："," 搜索框和结果展示样式可根据品牌和需求进行个性化定制‌.",[68,174,175,178],{},[71,176,177],{},"自动化‌："," 配置好后，自动化的索引更新减少了手动维护的工作量‌.",[68,180,181,184],{},[71,182,183],{},"社区支持‌："," 配置文件存储在GitCode上，方便分享和协作，同时也便于社区贡献者共同改进‌.",[123,186,188],{"id":187},"_32-应用场景","3.2 应用场景",[128,190,191,197,203,209],{},[68,192,193,196],{},[71,194,195],{},"开发文档："," 无论是开源项目的API参考，还是商业产品的用户指南，都能受益于DocSearch提供的强大搜索功能‌.",[68,198,199,202],{},[71,200,201],{},"教育平台‌："," 教学资源的查找可以通过DocSearch变得更简单快捷‌.",[68,204,205,208],{},[71,206,207],{},"博客聚合‌："," 博客聚合站点可以利用此服务提升读者的阅读体验‌.",[68,210,211,214],{},[71,212,213],{},"任何需要高效内部搜索的网站‌："," 无论大小，只要网站有大量文本内容，DocSearch都是理想选择‌.",[123,216,218],{"id":217},"_33-基本原理","3.3 基本原理",[13,220,221],{},"Algolia服务器会定期抓取我们指定的文档地址中的内容进行分析并建立索引，这样在网站搜索框中输入关键词后，前端会调用 Algolia DocSearch 的接口并显示搜索结果。这些请求、结果显示的相关逻辑都封装好了，我们要做的就是按要求插入代码、配置好网站样式以及搜索框。",[223,224,225],"note",{},[13,226,227,228,230],{},"根据 ",[42,229,109],{}," 的官方文档，默认情况下它通常一周爬取一次网站内容，用户也可自行触发爬取.",[123,232,234],{"id":233},"_34-使用要求","3.4 使用要求",[13,236,237],{},"Algolia DocSearch 提供的免费服务是需要申请的，当我们的网站满足下列条件时，Algolia 那边的工作人员才会让我们的申请通过：",[65,239,240,243,246,249],{},[68,241,242],{},"我们必须是云文档网站的所有者，网站必须是公开的。",[68,244,245],{},"网站内容必须是开源项目的技术文档或技术博客。",[68,247,248],{},"网站申请服务时必须有完整稳定的设计和内容，即确认网站做好生产准备。",[68,250,251],{},"免费计划限制，仅支持公开可访问的网站（如博客、开源项目文档），需通过简单申请流程获取API密钥。 ‌",[17,253,255],{"id":254},"_4-algolia和docsearch区别","4. Algolia和DocSearch区别",[13,257,258],{},"Algolia 是一个‌完整的商业搜索服务平台‌，而 DocSearch 是其旗下专门为‌技术文档场景定制的自动化搜索解决方案‌。两者的核心区别如下：",[260,261,262,277],"table",{},[263,264,265],"thead",{},[266,267,268,272,275],"tr",{},[269,270,271],"th",{},"对比项",[269,273,274],{},"Algolia",[269,276,95],{},[278,279,280,311,328,345,377],"tbody",{},[266,281,282,286,299],{},[283,284,285],"td",{},"定位与功能范围",[283,287,288,289,292,293,295,296,298],{},"通用型搜索引擎服务平台，支持电商、APP、企业数据等多种场景。核心功能为:",[290,291],"br",{},"  1. 分布式架构实现毫秒级响应‌。",[290,294],{}," 2. 智能排序、拼写纠错、同义词处理等算法‌。",[290,297],{}," 3.  开发者可自由定制索引规则和搜索交互‌。",[283,300,301,302,304,305,307,308,310],{},"Algolia 为‌技术文档\u002F博客‌设计的“开箱即用”方案。核心功能为：",[290,303],{}," 1. 自动爬取网站内容并构建索引‌。",[290,306],{}," 2. 提供预设的前端搜索组件(搜索框+结果页)。",[290,309],{}," 3. 仅适用于公开的技术文档类网站‌。",[266,312,313,316,322],{},[283,314,315],{},"数据接入方式",[283,317,318,319,321],{},"1. 需手动上传数据(如通过API或SDK)",[290,320],{}," 2. 使用官方爬虫工具 ‌Algolia Crawler‌ 自定义抓取逻辑‌。",[283,323,324,325,327],{},"1. 完全自动化,提交网站URL后，Algolia ‌自动定期爬取‌(默认每周一次)，可自定义定时任务执行时间。",[290,326],{},"2. 无需手动配置数据管道，适合文档更新频繁的场景‌。",[266,329,330,333,339],{},[283,331,332],{},"成本与适用性",[283,334,335,336,338],{},"1. 免费版限制 10,000 条记录‌。",[290,337],{}," 2. 高阶功能（如个性化排序）需付费‌",[283,340,341,342,344],{},"1. 对技术文档完全免费‌，无记录数量限制‌。",[290,343],{}," 2. 但需通过人工审核(网站需为公开技术内容)。",[266,346,347,350,365],{},[283,348,349],{},"集成复杂度",[283,351,352,353,355,356,358,359,361,362,364],{},"需自主开发，自行配置。",[290,354],{}," 1. 需要自己手动创建应用，认证爬取站点域名。",[290,357],{}," 2. 需要自己手动创建索引名称，自动导入数据。",[290,360],{}," 3. 需要自己编写爬虫代码。",[290,363],{},"4. 自己编写前端交互组件‌。",[283,366,367,368,370,371,373,374,376],{},"极简集成。",[290,369],{}," 1. DocSearch免费计划申请通过后，系统自行创建索引名称，认证站点域名，编写爬虫脚本。",[290,372],{},"2. 通过CDN引入CSS\u002FJS文件。",[290,375],{}," 3. 添加预设的搜索框HTML标签即可实现页面检索框弹框效果。",[266,378,379,382,385],{},[283,380,381],{},"搜索体验差异",[283,383,384],{},"需通过API参数和UI库手动实现同等效果‌。",[283,386,387,388,390,391,393],{},"1. 结果页自动高亮标题层级(h1 > h2 > h3)。",[290,389],{}," 2. 针对文档结构优化相关性排序(如优先匹配章节标题)。",[290,392],{}," 3. 内置响应式UI，适配移动端‌。",[17,395,397],{"id":396},"_5-docsearch使用说明","5. DocSearch使用说明",[123,399,401],{"id":400},"_51-申请免费资格","5.1 申请免费资格",[13,403,404,405,407,408,410,411,415],{},"首先，你需要申请 ",[42,406,274],{}," 免费的 ",[42,409,95],{},"计划资格。打开 ",[104,412,109],{"href":413,"rel":414,"target":28},"https:\u002F\u002Fdocsearch.algolia.com\u002F",[108]," 官网，点击 “Apply” 按钮，在表单项中填写你的项目信息，包括文档站的 URL（需包含协议头 https:\u002F\u002F）、邮箱、是否开源等。",[13,417,418],{},[419,420],"img",{"alt":421,"src":422},"申请表单","https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605174653671.webp",[13,424,425,426,428,429,431,432,435,436,439,440,443],{},"提交表单后，",[42,427,274],{}," 团队会审核你的申请(工作日一般是当天或者隔天就可申请通过,最迟不超过一周）。如果符合条件，他们会发邮件通知你，切记你需要回复邮件，确认你是网站站长，并且能够修改代码。通过确认后，他们会为你开通免费的 ",[42,430,95],{},"服务，Algolia DocSearch 会再发一封使用邮件，其中包含",[42,433,434],{},"appId","、",[42,437,438],{},"apiKey"," 和 ",[42,441,442],{},"indexName","，用于在网站生成框架中配置。若未回复，视为放弃请求。",[13,445,446],{},[419,447],{"alt":448,"src":449},"申请成功","https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605175441185.webp",[123,451,453],{"id":452},"_52-algolia管理后台","5.2 Algolia管理后台",[13,455,456,457,459,460,464,465,468,469,471],{},"免费的",[42,458,109],{},"计划申请通过后，我们也可以前往",[104,461,274],{"href":462,"rel":463,"target":28},"https:\u002F\u002Fdashboard.algolia.com\u002Fusers\u002Fsign_in",[108],"管理自己的后台数据，查询对应的",[42,466,467],{},"key","信息。",[42,470,109],{},"审核通过后，控制面板中会自动生成申请通过邮件返回的应用名称和应用id，默认索引名称，默认爬取程序。默认索引名称可以删除，系统审核通过时，已对申请域名进行了认证，认证后索引数据才能生效。默认爬取程序无法删除。",[65,473,474],{},[68,475,476,477,482,483,486,487,489,490,493,494,497],{},"进入",[104,478,481],{"href":479,"rel":480},"https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Findices",[108],"控制面板","后，点击左上角",[42,484,485],{},"Application","，切换申请通过的",[42,488,95],{},"应用。通过左侧",[42,491,492],{},"search","按钮查看应用索引信息，下方",[42,495,496],{}," Go To Crawler","进入爬虫程序页面",[13,499,500],{},[419,501],{"alt":502,"src":503},"","https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702162222617.webp",[65,505,507],{"start":506},2,[68,508,509],{},"索引数据页面",[13,511,512,513,516,517,519,520,523,524,527],{},"点击左侧选项栏里的",[42,514,515],{},"Search","进入索引页面，查看对应的",[42,518,442],{}," 数据，如果",[42,521,522],{},"Browse","这里没有显示数据，那说明爬取的数据可能有问题，导致没有生成对应的",[42,525,526],{},"Records","，那我们就需要排查第三步爬虫程序是否有问题。",[13,529,530],{},[419,531],{"alt":502,"src":532},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702162609300.webp",[65,534,536],{"start":535},3,[68,537,538],{},"爬虫程序页面",[13,540,541,542,544,545,548],{},"点击控制面板的",[42,543,496],{},"按钮或者左下方的",[42,546,547],{},"Data Source","按钮都可以进入爬虫程序页面。",[13,550,551],{},[419,552],{"alt":502,"src":553},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702164213759.webp",[13,555,556],{},"点击索引名称，进入爬虫配置页面",[13,558,559,562],{},[419,560],{"alt":502,"src":561},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165152222.webp",[419,563],{"alt":502,"src":564},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165158121.webp",[13,566,567,568,497],{},"点击",[42,569,570],{},"Editor",[13,572,573],{},[419,574],{"alt":502,"src":575},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165622704.webp",[13,577,578,579,582,583,585],{},"如果爬虫监控台显示成功爬取，也有",[42,580,581],{},"Monitoring Success"," 的数据，但 ",[42,584,526],{}," 为 0，那大概是爬虫提取数据的逻辑有问题，查看上图代码，查看具体的爬虫逻辑，查看是否存在错误信息。",[13,587,588,589,592,593,595,596,599,600,603],{},"像",[42,590,591],{},"pathsToMatch"," 这里是最容易出错的地方，比如: ",[42,594,591],{}," 如果是",[42,597,598],{},"https:\u002F\u002Fanyfork.github.io\u002Fblog-docs\u002Fdocs\u002F**","，但你的网址都是",[42,601,602],{},"https:\u002F\u002Fanyfork.github.io\u002Fblog-docs\u002F**","开头，那这里大概就是会提取错误，修改一下，然后点击右侧的数据进行测试。",[13,605,606],{},"一般情况下，基本都是爬虫程序爬取的域名或者路径不对导致爬虫失败，因此需要认真核对一下是否配置正确。",[65,608,610],{"start":609},4,[68,611,612,613,616],{},"点击左下角的",[42,614,615],{},"setting","查看应用配置信息。",[13,618,619],{},[419,620],{"alt":502,"src":621},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605190630560.webp",[65,623,625],{"start":624},5,[68,626,627,628,631],{},"点击右侧",[42,629,630],{},"API Keys","，查看应用的所有key信息，项目中集成会用到几个关键的key。",[13,633,634,637,638,641,642,644,645,648,649,651],{},[419,635],{"alt":502,"src":636},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605190902912.webp","\n目前项目使用",[42,639,640],{},"docSearch","只需要",[42,643,438],{},"，",[42,646,647],{},"applicationId","和",[42,650,442],{},"这3个key。",[17,653,655],{"id":654},"_6-nuxt3集成docsearch","6. Nuxt3集成DocSearch",[13,657,658,663,664,667,668,670],{},[104,659,662],{"href":660,"rel":661,"target":28},"https:\u002F\u002Falgolia.nuxtjs.org\u002Fadvanced\u002Fdocsearch\u002F",[108],"nuxt\u002Falgolia","是专为 Nuxt.js 框架设计的模块，用于简化与 Algolia 搜索引擎的集成。它提供了快速、灵活的搜索功能集成方案，适用于电商网站、博客平台、文档站点等场景，其中提供了在",[42,665,666],{},"Nuxt3","项目中使用",[42,669,95],{},"的配置教程。大概使用流畅如下：",[123,672,674],{"id":673},"_61-安装依赖","6.1 安装依赖",[676,677,678,705,721],"code-group",{},[679,680,685],"pre",{"className":681,"code":682,"filename":683,"language":684,"meta":502,"style":502},"language-bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","pnpm add @docsearch\u002Fjs @docsearch\u002Fcss\n","pnpm","bash",[42,686,687],{"__ignoreMap":502},[688,689,692,695,699,702],"span",{"class":690,"line":691},"line",1,[688,693,683],{"class":694},"sBMFI",[688,696,698],{"class":697},"sfazB"," add",[688,700,701],{"class":697}," @docsearch\u002Fjs",[688,703,704],{"class":697}," @docsearch\u002Fcss\n",[679,706,709],{"className":681,"code":707,"filename":708,"language":684,"meta":502,"style":502},"yarn add @docsearch\u002Fjs @docsearch\u002Fcss\n","yarn",[42,710,711],{"__ignoreMap":502},[688,712,713,715,717,719],{"class":690,"line":691},[688,714,708],{"class":694},[688,716,698],{"class":697},[688,718,701],{"class":697},[688,720,704],{"class":697},[679,722,725],{"className":681,"code":723,"filename":724,"language":684,"meta":502,"style":502},"npm install @docsearch\u002Fjs @docsearch\u002Fcss\n","npm",[42,726,727],{"__ignoreMap":502},[688,728,729,731,734,736],{"class":690,"line":691},[688,730,724],{"class":694},[688,732,733],{"class":697}," install",[688,735,701],{"class":697},[688,737,704],{"class":697},[123,739,741],{"id":740},"_62-安装模块","6.2 安装模块",[13,743,744,745,748,749,752,753,755,756,644,759,648,761,763],{},"将",[42,746,747],{"color":116},"@nuxtjs\u002Falgolia","安装到",[42,750,751],{},"nuxt","的模块中，并配置",[42,754,95],{},"申请成功后邮件返回的",[42,757,758],{},"apikey",[42,760,434],{},[42,762,442],{},"这3个key信息。",[679,765,770],{"className":766,"code":767,"filename":768,"language":769,"meta":502,"style":502},"language-ts shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","export default defineNuxtConfig({\n  modules:['@nuxtjs\u002Falgolia']\n  algolia: {\n    apiKey: 'apiKey',\n    applicationId: 'applicationId',\n    \u002F\u002F DocSearch key is used to configure DocSearch extension.\n    docSearch: {\n      indexName: 'indexName',\n    }\n  }  \n})\n","nuxt.config.ts","ts",[42,771,772,793,815,822,839,854,861,872,888,894,903],{"__ignoreMap":502},[688,773,774,778,781,785,789],{"class":690,"line":691},[688,775,777],{"class":776},"s7zQu","export",[688,779,780],{"class":776}," default",[688,782,784],{"class":783},"s2Zo4"," defineNuxtConfig",[688,786,788],{"class":787},"sTEyZ","(",[688,790,792],{"class":791},"sMK4o","{\n",[688,794,795,799,802,805,808,810,812],{"class":690,"line":506},[688,796,798],{"class":797},"swJcz","  modules",[688,800,801],{"class":791},":",[688,803,804],{"class":787},"[",[688,806,807],{"class":791},"'",[688,809,747],{"class":697},[688,811,807],{"class":791},[688,813,814],{"class":787},"]\n",[688,816,817,820],{"class":690,"line":535},[688,818,819],{"class":787},"  algolia: ",[688,821,792],{"class":791},[688,823,824,827,829,832,834,836],{"class":690,"line":609},[688,825,826],{"class":797},"    apiKey",[688,828,801],{"class":791},[688,830,831],{"class":791}," '",[688,833,438],{"class":697},[688,835,807],{"class":791},[688,837,838],{"class":791},",\n",[688,840,841,844,846,848,850,852],{"class":690,"line":624},[688,842,843],{"class":797},"    applicationId",[688,845,801],{"class":791},[688,847,831],{"class":791},[688,849,647],{"class":697},[688,851,807],{"class":791},[688,853,838],{"class":791},[688,855,857],{"class":690,"line":856},6,[688,858,860],{"class":859},"sHwdD","    \u002F\u002F DocSearch key is used to configure DocSearch extension.\n",[688,862,864,867,869],{"class":690,"line":863},7,[688,865,866],{"class":797},"    docSearch",[688,868,801],{"class":791},[688,870,871],{"class":791}," {\n",[688,873,875,878,880,882,884,886],{"class":690,"line":874},8,[688,876,877],{"class":797},"      indexName",[688,879,801],{"class":791},[688,881,831],{"class":791},[688,883,442],{"class":697},[688,885,807],{"class":791},[688,887,838],{"class":791},[688,889,891],{"class":690,"line":890},9,[688,892,893],{"class":791},"    }\n",[688,895,897,900],{"class":690,"line":896},10,[688,898,899],{"class":791},"  }",[688,901,902],{"class":787},"  \n",[688,904,906,909],{"class":690,"line":905},11,[688,907,908],{"class":791},"}",[688,910,911],{"class":787},")\n",[123,913,915],{"id":914},"_63-使用组件","6.3 使用组件",[13,917,918,919,922],{},"将组件",[42,920,921],{},"\u003CAlgoliaDocSearch \u002F>","放置在需要展示搜索的地方，页面上就会展示出搜索框，点击弹出一个模态框，输入关键字，即可全局进行文本搜索。",[679,924,929],{"className":925,"code":926,"filename":927,"language":928,"meta":502,"style":502},"language-vue shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u003Ctemplate>\n  \u003CAlgoliaDocSearch \u002F>\n\u003C\u002Ftemplate>\n","index.vue","vue",[42,930,931,942,953],{"__ignoreMap":502},[688,932,933,936,939],{"class":690,"line":691},[688,934,935],{"class":791},"\u003C",[688,937,938],{"class":797},"template",[688,940,941],{"class":791},">\n",[688,943,944,947,950],{"class":690,"line":506},[688,945,946],{"class":791},"  \u003C",[688,948,949],{"class":797},"AlgoliaDocSearch",[688,951,952],{"class":791}," \u002F>\n",[688,954,955,958,960],{"class":690,"line":535},[688,956,957],{"class":791},"\u003C\u002F",[688,959,938],{"class":797},[688,961,941],{"class":791},[13,963,964],{},[419,965],{"alt":502,"src":966},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702182342087.webp",[968,969,970],"style",{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":502,"searchDepth":535,"depth":535,"links":972},[973,974,975,981,982,986],{"id":19,"depth":506,"text":20},{"id":62,"depth":506,"text":63},{"id":99,"depth":506,"text":100,"children":976},[977,978,979,980],{"id":125,"depth":535,"text":126},{"id":187,"depth":535,"text":188},{"id":217,"depth":535,"text":218},{"id":233,"depth":535,"text":234},{"id":254,"depth":506,"text":255},{"id":396,"depth":506,"text":397,"children":983},[984,985],{"id":400,"depth":535,"text":401},{"id":452,"depth":535,"text":453},{"id":654,"depth":506,"text":655,"children":987},[988,989,990],{"id":673,"depth":535,"text":674},{"id":740,"depth":535,"text":741},{"id":914,"depth":535,"text":915},{"name":992,"symbol":492,"icon":993,"id":691},"全文检索","line-md:file-search","2025-06-05 15:34:23","‌ Algolia DocSearch为网站提供了高效、精准、免费的文档搜索服务功能，可以快速实现网站全文检索，尤其适合技术文档、博客等需要大量文本内容检索的站点，集成简单，使用方便。本文将系统的介绍Nuxt3项目中如何快速集成和使用DocSearch。",{"type":10,"value":997},[998],[13,999,1000],{},"Algolia 是一家专注于提供托管式搜索和实时数据查询服务的云服务平台‌，主要帮助网站、移动应用及技术文档快速集成高效、精准的搜索功能。其核心技术优势包括毫秒级响应速度、高度可定制的搜索算法、易用性强的API接口，以及对开源项目和非商业用途的免费支持。","md",false,true,{},"\u002Fblog\u002Falgolia-docsearch","---\ntitle: Algolia DocSearch快速开启网站全文检索功能\ndescription: ‌ Algolia DocSearch为网站提供了高效、精准、免费的文档搜索服务功能，可以快速实现网站全文检索，尤其适合技术文档、博客等需要大量文本内容检索的站点，集成简单，使用方便。本文将系统的介绍Nuxt3项目中如何快速集成和使用DocSearch。\narticleId: algolia-docSearch\ncreateDate: \"2025-06-05 15:34:23\"\nupdateDate: \"2026-03-11 22:22:52\"\narticleThumb: https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327202906034.webp\ntagIds: \n    - 1\ncategoryId: 1\nisTop: true\nisRecommend: true\nisHot: true\nisHidden: false\nnavigation: true\n---\n\nAlgolia 是一家专注于提供托管式搜索和实时数据查询服务的云服务平台‌，主要帮助网站、移动应用及技术文档快速集成高效、精准的搜索功能。其核心技术优势包括毫秒级响应速度、高度可定制的搜索算法、易用性强的API接口，以及对开源项目和非商业用途的免费支持。\n\u003C!--more-->\n\nAlgolia 的服务支持多种语言支持 Ruby、Rails、Python、PHP、JS、Java、Android、Objective-C 等多种语言，具备关键字输入智能容错功能，并提供有搜索排名配置，并具有实时快速的特点(Algolia 在全球 12 个数据中心均有部署)。Algolia 还为移动设备提供了离线搜索引擎，其 C++ SDK 可以嵌入到应用服务器端，这样即便没有网络连接应用也能提供搜索功能。\n\n## 1. 相关导航链接\n::card-group\n\n::card\n---\ntitle: DocSearch免费服务申请页面\nicon: i-simple-icons-github\nto: https:\u002F\u002Fdocsearch.algolia.com\u002Fapply\u002F\ntarget: _blank\n---\n点击跳转DocSearch免费搜索服务申请页面，申请通过后，可快速集成DocSearch到项目中。\n::\n\n::card\n---\ntitle: Algolia管理后台页面\nicon: i-simple-icons-github\nto: https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Fdashboard\ntarget: _blank\n---\n点击跳转Algolia管理后台，查看应用`keys`，管理Algolia应用，索引和记录数据。\n::\n\n::card\n---\ntitle: algolia\u002Fdocsearch 源码仓库\nicon: i-simple-icons-github\nto: https:\u002F\u002Fgithub.com\u002Falgolia\u002Fdocsearch\ntarget: _blank\n---\n点击跳转docsearch源码仓库，查看不同框架中docsearch的集成步骤和使用方式。\n::\n\n::card\n---\ntitle: Algolia爬虫后台页面\nicon: i-simple-icons-github\nto: https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Fcrawler\u002Fcrawlers\ntarget: _blank\n---\n点击跳转Algolia爬虫管理后台，查看爬虫程序执行结果，手动重新触发爬虫任务或者自定义爬虫程序。\n::\n\n::\n\n## 2. Algolia功能和特点\n1. **托管式全文搜索：** Algolia 提供完整的搜索服务后端，无需部署，API即用。开发者无需自行构建搜索引擎架构，只需调用API即可实现实时搜索、模糊匹配、多语言支持等功能，轻量级集成。API响应速度快，低延迟、精准度高、有免费计划支持以及易于集成。\n\n2. **性能与扩展性‌：** 支持每秒处理数千次查询，延迟低至20毫秒；‌‌可扩展至数十亿条数据，适用于电商、内容平台等高并发场景;\n\n3. **‌开发者友好性‌：** 提供JavaScript、Python、Java等主流语言的SDK; 支持与Hexo、Hugo、Apifox、VuePress、VitePress等开发工具无缝集成，配置时间可缩短至数分钟；\n\n4. **使用成本：**  免费版支持1万条索引和1万次\u002F月搜索请求，个人用户基本无需付费；技术文档与开源项目‌，通过申请免费`DocSearch`计划，自动化索引生成和搜索服务部署，显著提升文档用户体验；‌‌\n\n## 3. Algolia DocSearch\n[Algolia DocSearch](https:\u002F\u002Fdocsearch.algolia.com\u002Fdocs\u002Fwhat-is-docsearch){target=_blank} 是Algolia提供的免费的自动化全文搜索服务，旨在帮助开发者为他们的文档网站提供高效、精确的搜索体验。它通过智能爬虫抓取和索引在线文档，定时爬取指定网站，自动构建搜索索引，并结合Algolia的高性能搜索技术，提供即时的、上下文相关的搜索结果‌。\n\n对于开发者而言，不需要繁琐的配置，只需要申请`Algolia DocSearch`{color=primary}服务的免费计划后，用相关的 API 直接调用服务就行了。目前前端框架都有相对成熟的`Algolia DocSearch API`{color=primary}快速集成的第三方库，使用非常简单。对于使用者，只需要在网站检索框输入关键字，即可快速的搜索相应结果。\n\n\n### 3.1. 功能和特点\n- ‌**智能爬虫‌：** Algolia DocSearch使用定制的爬虫技术，能够深入到网站结构中，有效地抓取和提取文档信息。用户可以通过配置文件指导爬虫的工作，包括要抓取的页面、字段和过滤规则‌.\n- ‌**数据索引‌：** 爬虫抓取的信息会被上传至Algolia的搜索索引，这是一个经过优化的数据存储，适合快速查询。Algolia支持丰富的搜索功能，如拼写纠错、部分匹配和布尔运算等‌.\n- ‌**自定义搜索界面‌：** Algolia提供了JavaScript SDK，允许用户在自己的网站上轻松嵌入搜索框，并自定义搜索结果显示样式和交互体验‌.\n- ‌**持续更新‌：** 配合 GitCode 上的配置仓库，每次更新文档时，DocSearch可以自动或按需重新索引，确保搜索结果始终与最新内容同步‌.\n- **即时性‌：** Algolia搜索引擎设计用于提供毫秒级的响应速度‌.\n- **准确性‌：** 通过深度学习和自然语言处理技术，提高搜索结果的相关度‌.\n- **可定制化‌：** 搜索框和结果展示样式可根据品牌和需求进行个性化定制‌.\n- **自动化‌：** 配置好后，自动化的索引更新减少了手动维护的工作量‌.\n- **社区支持‌：** 配置文件存储在GitCode上，方便分享和协作，同时也便于社区贡献者共同改进‌.\n### 3.2 应用场景\n- **开发文档：** 无论是开源项目的API参考，还是商业产品的用户指南，都能受益于DocSearch提供的强大搜索功能‌.\n- **教育平台‌：** 教学资源的查找可以通过DocSearch变得更简单快捷‌.\n- **博客聚合‌：** 博客聚合站点可以利用此服务提升读者的阅读体验‌.\n- **任何需要高效内部搜索的网站‌：** 无论大小，只要网站有大量文本内容，DocSearch都是理想选择‌.\n\n\n### 3.3 基本原理\nAlgolia服务器会定期抓取我们指定的文档地址中的内容进行分析并建立索引，这样在网站搜索框中输入关键词后，前端会调用 Algolia DocSearch 的接口并显示搜索结果。这些请求、结果显示的相关逻辑都封装好了，我们要做的就是按要求插入代码、配置好网站样式以及搜索框。\n::note\n根据 `Algolia DocSearch` 的官方文档，默认情况下它通常一周爬取一次网站内容，用户也可自行触发爬取.\n::\n\n### 3.4 使用要求\nAlgolia DocSearch 提供的免费服务是需要申请的，当我们的网站满足下列条件时，Algolia 那边的工作人员才会让我们的申请通过：\n1. 我们必须是云文档网站的所有者，网站必须是公开的。\n2. 网站内容必须是开源项目的技术文档或技术博客。\n3. 网站申请服务时必须有完整稳定的设计和内容，即确认网站做好生产准备。\n4. 免费计划限制，仅支持公开可访问的网站（如博客、开源项目文档），需通过简单申请流程获取API密钥。 ‌\n\n## 4. Algolia和DocSearch区别\nAlgolia 是一个‌完整的商业搜索服务平台‌，而 DocSearch 是其旗下专门为‌技术文档场景定制的自动化搜索解决方案‌。两者的核心区别如下：\n| 对比项    | Algolia   | DocSearch                    |\n|---------|-----------|--------------------------|\n| 定位与功能范围  |    通用型搜索引擎服务平台，支持电商、APP、企业数据等多种场景。核心功能为:\u003C\u002Fbr>  1. 分布式架构实现毫秒级响应‌。\u003C\u002Fbr> 2. 智能排序、拼写纠错、同义词处理等算法‌。\u003C\u002Fbr> 3.  开发者可自由定制索引规则和搜索交互‌。    | Algolia 为‌技术文档\u002F博客‌设计的“开箱即用”方案。核心功能为：\u003C\u002Fbr> 1. 自动爬取网站内容并构建索引‌。\u003C\u002Fbr> 2. 提供预设的前端搜索组件(搜索框+结果页)。\u003C\u002Fbr> 3. 仅适用于公开的技术文档类网站‌。 |\n| 数据接入方式  | 1. 需手动上传数据(如通过API或SDK)\u003C\u002Fbr> 2. 使用官方爬虫工具 ‌Algolia Crawler‌ 自定义抓取逻辑‌。    | 1. 完全自动化,提交网站URL后，Algolia ‌自动定期爬取‌(默认每周一次)，可自定义定时任务执行时间。\u003C\u002Fbr>2. 无需手动配置数据管道，适合文档更新频繁的场景‌。 |\n| 成本与适用性 | 1. 免费版限制 10,000 条记录‌。\u003C\u002Fbr> 2. 高阶功能（如个性化排序）需付费‌ | 1. 对技术文档完全免费‌，无记录数量限制‌。\u003C\u002Fbr> 2. 但需通过人工审核(网站需为公开技术内容)。 |\n| 集成复杂度 | 需自主开发，自行配置。\u003Cbr\u002F> 1. 需要自己手动创建应用，认证爬取站点域名。\u003Cbr\u002F> 2. 需要自己手动创建索引名称，自动导入数据。\u003Cbr> 3. 需要自己编写爬虫代码。\u003Cbr\u002F>4. 自己编写前端交互组件‌。 | 极简集成。\u003Cbr> 1. DocSearch免费计划申请通过后，系统自行创建索引名称，认证站点域名，编写爬虫脚本。\u003Cbr\u002F>2. 通过CDN引入CSS\u002FJS文件。\u003Cbr\u002F> 3. 添加预设的搜索框HTML标签即可实现页面检索框弹框效果。 |\n| 搜索体验差异 | 需通过API参数和UI库手动实现同等效果‌。 | 1. 结果页自动高亮标题层级(h1 > h2 > h3)。\u003Cbr\u002F> 2. 针对文档结构优化相关性排序(如优先匹配章节标题)。\u003Cbr\u002F> 3. 内置响应式UI，适配移动端‌。 |\n\n\n## 5. DocSearch使用说明\n### 5.1 申请免费资格\n首先，你需要申请 `Algolia` 免费的 `DocSearch`计划资格。打开 [Algolia DocSearch](https:\u002F\u002Fdocsearch.algolia.com\u002F){target=_blank} 官网，点击 “Apply” 按钮，在表单项中填写你的项目信息，包括文档站的 URL（需包含协议头 https:\u002F\u002F）、邮箱、是否开源等。\n\n![申请表单](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605174653671.webp)\n\n提交表单后，`Algolia` 团队会审核你的申请(工作日一般是当天或者隔天就可申请通过,最迟不超过一周）。如果符合条件，他们会发邮件通知你，切记你需要回复邮件，确认你是网站站长，并且能够修改代码。通过确认后，他们会为你开通免费的 `DocSearch`服务，Algolia DocSearch 会再发一封使用邮件，其中包含`appId`、`apiKey` 和 `indexName`，用于在网站生成框架中配置。若未回复，视为放弃请求。\n\n![申请成功](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605175441185.webp)\n### 5.2 Algolia管理后台\n免费的`Algolia DocSearch`计划申请通过后，我们也可以前往[Algolia](https:\u002F\u002Fdashboard.algolia.com\u002Fusers\u002Fsign_in){target=_blank}管理自己的后台数据，查询对应的`key`信息。`Algolia DocSearch`审核通过后，控制面板中会自动生成申请通过邮件返回的应用名称和应用id，默认索引名称，默认爬取程序。默认索引名称可以删除，系统审核通过时，已对申请域名进行了认证，认证后索引数据才能生效。默认爬取程序无法删除。\n\n1. 进入[控制面板](https:\u002F\u002Fdashboard.algolia.com\u002Fapps\u002FOBYO5BJ5I3\u002Findices)后，点击左上角`Application`，切换申请通过的`DocSearch`应用。通过左侧`search`按钮查看应用索引信息，下方` Go To Crawler`进入爬虫程序页面\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702162222617.webp)\n\n\n2. 索引数据页面\n\n\n点击左侧选项栏里的`Search`进入索引页面，查看对应的`indexName` 数据，如果`Browse`这里没有显示数据，那说明爬取的数据可能有问题，导致没有生成对应的`Records`，那我们就需要排查第三步爬虫程序是否有问题。\n\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702162609300.webp)\n\n\n3. 爬虫程序页面\n\n\n点击控制面板的` Go To Crawler`按钮或者左下方的`Data Source`按钮都可以进入爬虫程序页面。\n\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702164213759.webp)\n\n\n点击索引名称，进入爬虫配置页面\n\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165152222.webp)\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165158121.webp)\n\n\n点击`Editor`进入爬虫程序页面\n\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702165622704.webp)\n\n\n如果爬虫监控台显示成功爬取，也有`Monitoring Success` 的数据，但 `Records` 为 0，那大概是爬虫提取数据的逻辑有问题，查看上图代码，查看具体的爬虫逻辑，查看是否存在错误信息。\n\n\n像`pathsToMatch` 这里是最容易出错的地方，比如: `pathsToMatch` 如果是`https:\u002F\u002Fanyfork.github.io\u002Fblog-docs\u002Fdocs\u002F**`，但你的网址都是`https:\u002F\u002Fanyfork.github.io\u002Fblog-docs\u002F**`开头，那这里大概就是会提取错误，修改一下，然后点击右侧的数据进行测试。\n\n\n一般情况下，基本都是爬虫程序爬取的域名或者路径不对导致爬虫失败，因此需要认真核对一下是否配置正确。\n\n\n\n4. 点击左下角的`setting`查看应用配置信息。\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605190630560.webp)\n\n5. 点击右侧`API Keys`，查看应用的所有key信息，项目中集成会用到几个关键的key。\n\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250605190902912.webp)\n目前项目使用`docSearch`只需要`apiKey`，`applicationId`和`indexName`这3个key。\n\n\n## 6. Nuxt3集成DocSearch\n\n[nuxt\u002Falgolia](https:\u002F\u002Falgolia.nuxtjs.org\u002Fadvanced\u002Fdocsearch\u002F){target=_blank}是专为 Nuxt.js 框架设计的模块，用于简化与 Algolia 搜索引擎的集成。它提供了快速、灵活的搜索功能集成方案，适用于电商网站、博客平台、文档站点等场景，其中提供了在`Nuxt3`项目中使用`DocSearch`的配置教程。大概使用流畅如下：\n\n\n### 6.1 安装依赖\n::code-group\n\n```bash [pnpm]\npnpm add @docsearch\u002Fjs @docsearch\u002Fcss\n```\n\n```bash [yarn]\nyarn add @docsearch\u002Fjs @docsearch\u002Fcss\n```\n\n```bash [npm]\nnpm install @docsearch\u002Fjs @docsearch\u002Fcss\n```\n::\n\n\n### 6.2 安装模块\n将`@nuxtjs\u002Falgolia`{color=primary}安装到`nuxt`的模块中，并配置`DocSearch`申请成功后邮件返回的`apikey`，`appId`和`indexName`这3个key信息。\n```ts[nuxt.config.ts]\nexport default defineNuxtConfig({\n  modules:['@nuxtjs\u002Falgolia']\n  algolia: {\n    apiKey: 'apiKey',\n    applicationId: 'applicationId',\n    \u002F\u002F DocSearch key is used to configure DocSearch extension.\n    docSearch: {\n      indexName: 'indexName',\n    }\n  }  \n})\n```\n### 6.3 使用组件\n将组件`\u003CAlgoliaDocSearch \u002F>`放置在需要展示搜索的地方，页面上就会展示出搜索框，点击弹出一个模态框，输入关键字，即可全局进行文本搜索。\n```vue[index.vue]\n\u003Ctemplate>\n  \u003CAlgoliaDocSearch \u002F>\n\u003C\u002Ftemplate>\n```\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250702182342087.webp)",19.4,{"title":6,"description":995},{"loc":1005,"images":1010},[1011,1012,1013,1014,1015,1016,1017,1018,1019,1020,1021],{"loc":422},{"loc":449},{"loc":503},{"loc":532},{"loc":553},{"loc":561},{"loc":564},{"loc":575},{"loc":621},{"loc":636},{"loc":966},"blog\u002F1.Algolia DocSearch快速开启网站全文检索功能",[691],[1025],{"name":274,"icon":1026,"symbol":1027,"id":691},"devicon:algolia","algolia","2026-03-11 22:22:52",3488,"nkMbsKJW2vlLELpQgBwRgA5nr7Mv_B9uLj-IijWdPAk",{"id":1032,"title":1033,"articleId":1034,"articleThumb":1035,"body":1036,"categoryId":905,"categoryInfo":1932,"createDate":1936,"description":1937,"excerpt":1938,"extension":1001,"isHidden":1002,"isHot":1003,"isRecommend":1002,"isTop":1002,"meta":1943,"navigation":1003,"path":1944,"rawbody":1945,"readingTime":1946,"seo":1947,"sitemap":1948,"stem":1954,"tagIds":1955,"tagInfo":1956,"updateDate":1960,"wordCount":1961,"__hash__":1962},"blog\u002Fblog\u002F6.GitHub克隆拉取和推送代码时443超时问题解决方法.md","GitHub克隆拉取和推送代码时443超时问题解决方法","github-timeout","https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327204841213.webp",{"type":10,"value":1037,"toc":1921},[1038,1042,1045,1048,1052,1055,1070,1075,1078,1090,1095,1099,1110,1117,1130,1149,1155,1168,1175,1181,1197,1200,1214,1220,1497,1501,1511,1514,1564,1567,1593,1597,1600,1604,1614,1618,1623,1626,1672,1681,1720,1725,1728,1753,1761,1766,1769,1801,1804,1829,1834,1837,1879,1881,1915,1918],[17,1039,1041],{"id":1040},"_1-修改hosts文件","1 修改hosts文件",[13,1043,1044],{},"Hosts文件是系统中用于将网址域名与IP地址进行快速映射的数据库，在用户访问网站时优先被查询。这个没有扩展名的系统文件，虽然不起眼，却扮演着至关重要的角色。它类似于一个关联“数据库”，将众多常用的网址域名与对应的IP地址紧密相连。每当用户在浏览器中输入一个网址并尝试登录时，系统会首先自动在Hosts文件中寻找相应的IP地址。一旦找到，系统会迅速打开对应的网页；若未找到，则会自动将网址提交至DNS域名解析服务器进行IP地址的深入解析。",[13,1046,1047],{},"在解决 GitHub 在提交代码时出现 443 错误的问题时，修改 hosts 文件是一个可能的解决方案。这通常发生在 GitHub 的 IP 地址被错误地解析或者 DNS 解析出现问题的情况下，导致连接超时问题。通过修改hosts文件后，你可以绕过国内的访问限制，当尝试访问github及其相关域名时，系统将优先使用这些映射，从而可能解决网络连通性问题。以下是具体步骤：",[123,1049,1051],{"id":1050},"_11-查找github的ip地址","1.1 查找GitHub的IP地址",[13,1053,1054],{},"首先，你需要找到 GitHub 的 IP 地址。可以通过运行以下命令来获取 GitHub 的 IP 地址：",[679,1056,1060],{"className":1057,"code":1058,"language":1059,"meta":502,"style":502},"language-Bash shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","ping github.com\n","Bash",[42,1061,1062],{"__ignoreMap":502},[688,1063,1064,1067],{"class":690,"line":691},[688,1065,1066],{"class":694},"ping",[688,1068,1069],{"class":697}," github.com\n",[13,1071,1072],{},[419,1073],{"alt":502,"src":1074},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250629165553736.webp",[13,1076,1077],{},"或者，你可以使用以下命令获取更多 IP 地址：",[679,1079,1081],{"className":1057,"code":1080,"language":1059,"meta":502,"style":502},"nslookup github.com\n",[42,1082,1083],{"__ignoreMap":502},[688,1084,1085,1088],{"class":690,"line":691},[688,1086,1087],{"class":694},"nslookup",[688,1089,1069],{"class":697},[13,1091,1092],{},[419,1093],{"alt":502,"src":1094},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250629165553775.webp",[123,1096,1098],{"id":1097},"_12-手动编辑hosts文件","1.2 手动编辑hosts文件",[13,1100,1101,1102,1105,1106,1109],{},"在不同操作系统中，Hosts文件的位置各有不同，便于管理和编辑。例如，在Windows系统中，它通常位于",[42,1103,1104],{"color":116},"C:\\Windows\\System32\\drivers\\etc\\hosts","路径下；而在Android和iOS系统中，它则分别位于",[42,1107,1108],{"color":116},"\u002Fetc\u002Fhosts和\u002Fprivate\u002Fetc\u002Fhosts","等路径。对于Mac OS X系统，其位置则可能因版本不同而有所差异。",[128,1111,1112],{},[68,1113,1114],{},[71,1115,1116],{},"对于Windows系统",[13,1118,1119,1120,1123,1124,1126,1127,1129],{},"1、",[71,1121,1122],{},"以管理员权限身份","，通过记事本打开",[42,1125,1104],{"color":116},"文件。",[290,1128],{},"\n2、在文件末尾添加以下行（替换 \u003Cgithub_ip> 为你从 ping 或 nslookup 命令中获取的 IP 地址）:",[679,1131,1133],{"className":681,"code":1132,"language":684,"meta":502,"style":502}," \u003Cgithub_ip> github.com\n",[42,1134,1135],{"__ignoreMap":502},[688,1136,1137,1140,1143,1146],{"class":690,"line":691},[688,1138,1139],{"class":791}," \u003C",[688,1141,1142],{"class":694},"github_ip",[688,1144,1145],{"class":787},"> ",[688,1147,1148],{"class":697},"github.com\n",[13,1150,1151,1152,1154],{},"3、保存修改后的配置文件，并关闭文件。",[290,1153],{},"\n4、为了确保更改生效，你可以清空 DNS 缓存，在终端刷新本地DNS缓存：",[679,1156,1158],{"className":681,"code":1157,"language":684,"meta":502,"style":502},"ipconfig \u002Fflushdns\n",[42,1159,1160],{"__ignoreMap":502},[688,1161,1162,1165],{"class":690,"line":691},[688,1163,1164],{"class":694},"ipconfig",[688,1166,1167],{"class":697}," \u002Fflushdns\n",[128,1169,1170],{},[68,1171,1172],{},[71,1173,1174],{},"对于 macOS 或 Linux",[13,1176,1177,1178,1180],{},"1、打开终端。",[290,1179],{},"\n2、使用文本编辑器打开 \u002Fetc\u002Fhosts 文件，例如使用命令：",[679,1182,1184],{"className":681,"code":1183,"language":684,"meta":502,"style":502}," sudo vi \u002Fetc\u002Fhosts\n",[42,1185,1186],{"__ignoreMap":502},[688,1187,1188,1191,1194],{"class":690,"line":691},[688,1189,1190],{"class":694}," sudo",[688,1192,1193],{"class":697}," vi",[688,1195,1196],{"class":697}," \u002Fetc\u002Fhosts\n",[13,1198,1199],{},"3、在文件末尾添加以下行（同样替换 \u003Cgithub_ip>）：",[679,1201,1202],{"className":681,"code":1132,"language":684,"meta":502,"style":502},[42,1203,1204],{"__ignoreMap":502},[688,1205,1206,1208,1210,1212],{"class":690,"line":691},[688,1207,1139],{"class":791},[688,1209,1142],{"class":694},[688,1211,1145],{"class":787},[688,1213,1148],{"class":697},[13,1215,1216,1217,1219],{},"4、保存修改后的配置文件，并关闭文件。",[290,1218],{},"\n5、在 macOS 或 Linux 上，通常不需要手动清空 DNS 缓存，因为系统会自动处理。但如果你怀疑有缓存问题，可以尝试重启你的计算机。",[1221,1222,1224,1241,1494],"callout",{"color":116,"icon":1223},"circum:warning",[13,1225,1226,1227,1229,1230,1232,1233,1236,1237,1240],{},"温馨提示",[290,1228],{},"\n1 修改hosts方法可能不太稳定，但至少在一定程度上能够解决问题。如果过一段时间后，发现修改hosts也无法连接，需要重新修改hosts文件中ip和github映射关系。",[290,1231],{},"\n2 如果",[42,1234,1235],{},"hosts","文件中加入",[42,1238,1239],{},"\u003Cgithub ip> github.com","后发现还是连接不通，可以尝试加入以下IP地址和域名映射关系：",[679,1242,1244],{"className":681,"code":1243,"language":684,"meta":502,"style":502},"185.199.108.154   github.githubassets.com\n140.82.113.22     central.github.com\n185.199.108.133   desktop.githubusercontent.com\n185.199.109.153   assets-cdn.github.com\n185.199.110.133   camo.githubusercontent.com\n185.199.108.133   github.map.fastly.net\n151.101.201.194   github.global.ssl.fastly.net\n140.82.116.3      gist.github.com\n185.199.108.153   github.io\n140.82.116.3      github.com\n140.82.116.6      api.github.com\n185.199.111.133   raw.githubusercontent.com\n185.199.108.133   user-images.githubusercontent.com\n185.199.108.133   favicons.githubusercontent.com\n185.199.110.133   avatars5.githubusercontent.com\n185.199.108.133   avatars4.githubusercontent.com\n185.199.108.133   avatars3.githubusercontent.com\n185.199.109.133   avatars2.githubusercontent.com\n185.199.108.133   avatars1.githubusercontent.com\n185.199.108.133   avatars0.githubusercontent.com\n185.199.109.133   avatars.githubusercontent.com\n140.82.116.9      codeload.github.com\n52.217.135.153    github-cloud.s3.amazonaws.com\n3.5.21.109        github-com.s3.amazonaws.com\n3.5.29.177        github-production-release-asset-2e65be.s3.amazonaws.com\n52.216.63.25      github-production-user-asset-6210df.s3.amazonaws.com\n52.217.223.1      github-production-repository-file-5c1aeb.s3.amazonaws.com\n185.199.110.153   githubstatus.com\n140.82.112.17     github.community\n185.199.108.133   media.githubusercontent.com\n",[42,1245,1246,1254,1262,1270,1278,1286,1293,1301,1309,1317,1324,1332,1341,1349,1357,1365,1373,1381,1390,1398,1406,1414,1423,1432,1441,1450,1459,1468,1477,1486],{"__ignoreMap":502},[688,1247,1248,1251],{"class":690,"line":691},[688,1249,1250],{"class":694},"185.199.108.154",[688,1252,1253],{"class":697},"   github.githubassets.com\n",[688,1255,1256,1259],{"class":690,"line":506},[688,1257,1258],{"class":694},"140.82.113.22",[688,1260,1261],{"class":697},"     central.github.com\n",[688,1263,1264,1267],{"class":690,"line":535},[688,1265,1266],{"class":694},"185.199.108.133",[688,1268,1269],{"class":697},"   desktop.githubusercontent.com\n",[688,1271,1272,1275],{"class":690,"line":609},[688,1273,1274],{"class":694},"185.199.109.153",[688,1276,1277],{"class":697},"   assets-cdn.github.com\n",[688,1279,1280,1283],{"class":690,"line":624},[688,1281,1282],{"class":694},"185.199.110.133",[688,1284,1285],{"class":697},"   camo.githubusercontent.com\n",[688,1287,1288,1290],{"class":690,"line":856},[688,1289,1266],{"class":694},[688,1291,1292],{"class":697},"   github.map.fastly.net\n",[688,1294,1295,1298],{"class":690,"line":863},[688,1296,1297],{"class":694},"151.101.201.194",[688,1299,1300],{"class":697},"   github.global.ssl.fastly.net\n",[688,1302,1303,1306],{"class":690,"line":874},[688,1304,1305],{"class":694},"140.82.116.3",[688,1307,1308],{"class":697},"      gist.github.com\n",[688,1310,1311,1314],{"class":690,"line":890},[688,1312,1313],{"class":694},"185.199.108.153",[688,1315,1316],{"class":697},"   github.io\n",[688,1318,1319,1321],{"class":690,"line":896},[688,1320,1305],{"class":694},[688,1322,1323],{"class":697},"      github.com\n",[688,1325,1326,1329],{"class":690,"line":905},[688,1327,1328],{"class":694},"140.82.116.6",[688,1330,1331],{"class":697},"      api.github.com\n",[688,1333,1335,1338],{"class":690,"line":1334},12,[688,1336,1337],{"class":694},"185.199.111.133",[688,1339,1340],{"class":697},"   raw.githubusercontent.com\n",[688,1342,1344,1346],{"class":690,"line":1343},13,[688,1345,1266],{"class":694},[688,1347,1348],{"class":697},"   user-images.githubusercontent.com\n",[688,1350,1352,1354],{"class":690,"line":1351},14,[688,1353,1266],{"class":694},[688,1355,1356],{"class":697},"   favicons.githubusercontent.com\n",[688,1358,1360,1362],{"class":690,"line":1359},15,[688,1361,1282],{"class":694},[688,1363,1364],{"class":697},"   avatars5.githubusercontent.com\n",[688,1366,1368,1370],{"class":690,"line":1367},16,[688,1369,1266],{"class":694},[688,1371,1372],{"class":697},"   avatars4.githubusercontent.com\n",[688,1374,1376,1378],{"class":690,"line":1375},17,[688,1377,1266],{"class":694},[688,1379,1380],{"class":697},"   avatars3.githubusercontent.com\n",[688,1382,1384,1387],{"class":690,"line":1383},18,[688,1385,1386],{"class":694},"185.199.109.133",[688,1388,1389],{"class":697},"   avatars2.githubusercontent.com\n",[688,1391,1393,1395],{"class":690,"line":1392},19,[688,1394,1266],{"class":694},[688,1396,1397],{"class":697},"   avatars1.githubusercontent.com\n",[688,1399,1401,1403],{"class":690,"line":1400},20,[688,1402,1266],{"class":694},[688,1404,1405],{"class":697},"   avatars0.githubusercontent.com\n",[688,1407,1409,1411],{"class":690,"line":1408},21,[688,1410,1386],{"class":694},[688,1412,1413],{"class":697},"   avatars.githubusercontent.com\n",[688,1415,1417,1420],{"class":690,"line":1416},22,[688,1418,1419],{"class":694},"140.82.116.9",[688,1421,1422],{"class":697},"      codeload.github.com\n",[688,1424,1426,1429],{"class":690,"line":1425},23,[688,1427,1428],{"class":694},"52.217.135.153",[688,1430,1431],{"class":697},"    github-cloud.s3.amazonaws.com\n",[688,1433,1435,1438],{"class":690,"line":1434},24,[688,1436,1437],{"class":694},"3.5.21.109",[688,1439,1440],{"class":697},"        github-com.s3.amazonaws.com\n",[688,1442,1444,1447],{"class":690,"line":1443},25,[688,1445,1446],{"class":694},"3.5.29.177",[688,1448,1449],{"class":697},"        github-production-release-asset-2e65be.s3.amazonaws.com\n",[688,1451,1453,1456],{"class":690,"line":1452},26,[688,1454,1455],{"class":694},"52.216.63.25",[688,1457,1458],{"class":697},"      github-production-user-asset-6210df.s3.amazonaws.com\n",[688,1460,1462,1465],{"class":690,"line":1461},27,[688,1463,1464],{"class":694},"52.217.223.1",[688,1466,1467],{"class":697},"      github-production-repository-file-5c1aeb.s3.amazonaws.com\n",[688,1469,1471,1474],{"class":690,"line":1470},28,[688,1472,1473],{"class":694},"185.199.110.153",[688,1475,1476],{"class":697},"   githubstatus.com\n",[688,1478,1480,1483],{"class":690,"line":1479},29,[688,1481,1482],{"class":694},"140.82.112.17",[688,1484,1485],{"class":697},"     github.community\n",[688,1487,1489,1491],{"class":690,"line":1488},30,[688,1490,1266],{"class":694},[688,1492,1493],{"class":697},"   media.githubusercontent.com\n",[13,1495,1496],{},"上面具体域名对应的映射ip，可以通过命令行ping的方式获取。",[123,1498,1500],{"id":1499},"_13-工具编辑hosts文件","1.3 工具编辑hosts文件",[13,1502,1503,1504,1507,1508,1510],{},"对于windows系统可以借助第三方工具",[42,1505,1506],{},"WinHostsManager","进行修改",[42,1509,1235],{},"文件。‌ WinHostsManager 是一款功能强大的hosts文件管理工具，主要用于管理和修改hosts文件。‌",[13,1512,1513],{},"WinHostsManager的主要功能包括：",[65,1515,1516,1522,1528,1534,1540,1546,1552,1558],{},[68,1517,1518,1521],{},[71,1519,1520],{},"添加新的host项目‌："," 用户可以轻松添加新的IP地址和域名映射。",[68,1523,1524,1527],{},[71,1525,1526],{},"保存修改‌："," 将新的IP地址和域名直接加载到hosts文件中并保存",[68,1529,1530,1533],{},[71,1531,1532],{},"显示最高映射方案‌："," 提供当前最高的映射方案，方便用户选择。",[68,1535,1536,1539],{},[71,1537,1538],{},"获取最快的主机IP编辑方式‌："," 优化编辑过程，提高编辑效率。",[68,1541,1542,1545],{},[71,1543,1544],{},"支持重启主机‌："," 编辑完成后，支持重启主机以应用更改",[68,1547,132,1548,1551],{},[71,1549,1550],{},"查看端口项目‌："," 在列表上查看打开的端口项目。",[68,1553,132,1554,1557],{},[71,1555,1556],{},"建立过滤‌："," 提供过滤功能，方便管理hosts文件‌。",[68,1559,1560,1563],{},[71,1561,1562],{},"支持禁用ip：","  提供启用和禁用ip和域名映射关系功能。",[13,1565,1566],{},"此外，WinHostsManager还具有以下特点：",[128,1568,1569,1575,1581,1587],{},[68,1570,1571,1574],{},[71,1572,1573],{},"操作简单‌："," 用户无需知道hosts文件的保存位置，即可进行修改。",[68,1576,1577,1580],{},[71,1578,1579],{},"节省时间‌："," 软件会自动将内容写入hosts文件，节省了在C盘上查找hosts文件地址的时间。",[68,1582,1583,1586],{},[71,1584,1585],{},"支持特殊地址访问‌："," 帮助用户访问一些特殊地址。",[68,1588,1589,1592],{},[71,1590,1591],{},"备份和恢复功能‌："," 支持hosts文件的备份和恢复，确保数据安全‌。",[17,1594,1596],{"id":1595},"_2-配置git代理","2 配置GIT代理",[13,1598,1599],{},"通常我们访问GitHub站点都需要本地打开VPN工具才能访问，但即使打开VPN工具，打开了全局代理模式，git拉取或者提交代码依然报443连接错误。在Git中配置代理服务器是为了帮助你通过一个HTTP或HTTPS代理连接到GitHub外部网络资源，根据你的网络环境，你可能需要设置代理来绕过某些限制或提高访问速度，才能让git客户端通过VPN代理渠道访问github，否则git客户端不走VPN代理就会出现连接错误。",[123,1601,1603],{"id":1602},"_21-vpn代理ip和端口","2.1 VPN代理IP和端口",[13,1605,1606,1607,1610,1611],{},"打开本地VPN工具，查看设置页面http(s)代理IP和端口。以海豚湾为例，查看方法如下图：\n",[419,1608],{"alt":502,"src":1609},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250701092746871.webp","\n或者查看电脑本地代理端口，查看方法如下图：\n",[419,1612],{"alt":502,"src":1613},"https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250701092752202.webp",[123,1615,1617],{"id":1616},"_22-git本地代理配置","2.2 GIT本地代理配置",[13,1619,1620],{},[71,1621,1622],{},"1. 全局代理配置",[13,1624,1625],{},"如果你希望对所有Git操作使用同一个代理服务器，你可以通过以下命令格式配置全局代理：",[679,1627,1629],{"className":681,"code":1628,"language":684,"meta":502,"style":502},"# 配置http代理\ngit config --global http.proxy http:\u002F\u002Fproxy_address:port\n# 配置https代理\ngit config --global https.proxy https:\u002F\u002Fproxy_address:port\n",[42,1630,1631,1636,1653,1658],{"__ignoreMap":502},[688,1632,1633],{"class":690,"line":691},[688,1634,1635],{"class":859},"# 配置http代理\n",[688,1637,1638,1641,1644,1647,1650],{"class":690,"line":506},[688,1639,1640],{"class":694},"git",[688,1642,1643],{"class":697}," config",[688,1645,1646],{"class":697}," --global",[688,1648,1649],{"class":697}," http.proxy",[688,1651,1652],{"class":697}," http:\u002F\u002Fproxy_address:port\n",[688,1654,1655],{"class":690,"line":535},[688,1656,1657],{"class":859},"# 配置https代理\n",[688,1659,1660,1662,1664,1666,1669],{"class":690,"line":609},[688,1661,1640],{"class":694},[688,1663,1643],{"class":697},[688,1665,1646],{"class":697},[688,1667,1668],{"class":697}," https.proxy",[688,1670,1671],{"class":697}," https:\u002F\u002Fproxy_address:port\n",[13,1673,744,1674,648,1677,1680],{},[42,1675,1676],{},"proxy_address",[42,1678,1679],{},"port","替换为你的代理服务器的地址和端口。例如：您正在使用127.0.0.1的代理服务器和4780的端口，那么您需要输入以下命令：",[679,1682,1684],{"className":681,"code":1683,"language":684,"meta":502,"style":502},"# 配置http代理\ngit config --global http.proxy http:\u002F\u002F127.0.0.1:4780\n# 配置https代理\ngit config --global https.proxy https:\u002F\u002F127.0.0.1:4780\n",[42,1685,1686,1690,1703,1707],{"__ignoreMap":502},[688,1687,1688],{"class":690,"line":691},[688,1689,1635],{"class":859},[688,1691,1692,1694,1696,1698,1700],{"class":690,"line":506},[688,1693,1640],{"class":694},[688,1695,1643],{"class":697},[688,1697,1646],{"class":697},[688,1699,1649],{"class":697},[688,1701,1702],{"class":697}," http:\u002F\u002F127.0.0.1:4780\n",[688,1704,1705],{"class":690,"line":535},[688,1706,1657],{"class":859},[688,1708,1709,1711,1713,1715,1717],{"class":690,"line":609},[688,1710,1640],{"class":694},[688,1712,1643],{"class":697},[688,1714,1646],{"class":697},[688,1716,1668],{"class":697},[688,1718,1719],{"class":697}," https:\u002F\u002F127.0.0.1:4780\n",[13,1721,1722],{},[71,1723,1724],{},"2. 单个仓库代理配置",[13,1726,1727],{},"如果你只想为特定的Git仓库设置代理，而不是全局，你需要进入到本地具体代码仓库目录下，使用以下命令：",[679,1729,1731],{"className":681,"code":1730,"language":684,"meta":502,"style":502},"git config http.proxy http:\u002F\u002Fproxy_address:port\ngit config https.proxy https:\u002F\u002Fproxy_address:port\n",[42,1732,1733,1743],{"__ignoreMap":502},[688,1734,1735,1737,1739,1741],{"class":690,"line":691},[688,1736,1640],{"class":694},[688,1738,1643],{"class":697},[688,1740,1649],{"class":697},[688,1742,1652],{"class":697},[688,1744,1745,1747,1749,1751],{"class":690,"line":506},[688,1746,1640],{"class":694},[688,1748,1643],{"class":697},[688,1750,1668],{"class":697},[688,1752,1671],{"class":697},[13,1754,1755,1756,648,1758,1760],{},"同样地，替换",[42,1757,1676],{},[42,1759,1679],{},"为你的代理服务器信息。",[13,1762,1763],{},[71,1764,1765],{},"3. 取消代理配置",[13,1767,1768],{},"如果你需要取消之前设置的代理配置，可以使用以下命令：",[679,1770,1772],{"className":681,"code":1771,"language":684,"meta":502,"style":502},"git config --global --unset http.proxy\ngit config --global --unset https.proxy\n",[42,1773,1774,1788],{"__ignoreMap":502},[688,1775,1776,1778,1780,1782,1785],{"class":690,"line":691},[688,1777,1640],{"class":694},[688,1779,1643],{"class":697},[688,1781,1646],{"class":697},[688,1783,1784],{"class":697}," --unset",[688,1786,1787],{"class":697}," http.proxy\n",[688,1789,1790,1792,1794,1796,1798],{"class":690,"line":506},[688,1791,1640],{"class":694},[688,1793,1643],{"class":697},[688,1795,1646],{"class":697},[688,1797,1784],{"class":697},[688,1799,1800],{"class":697}," https.proxy\n",[13,1802,1803],{},"或者针对单个仓库：",[679,1805,1807],{"className":681,"code":1806,"language":684,"meta":502,"style":502},"git config --unset http.proxy\ngit config --unset https.proxy\n",[42,1808,1809,1819],{"__ignoreMap":502},[688,1810,1811,1813,1815,1817],{"class":690,"line":691},[688,1812,1640],{"class":694},[688,1814,1643],{"class":697},[688,1816,1784],{"class":697},[688,1818,1787],{"class":697},[688,1820,1821,1823,1825,1827],{"class":690,"line":506},[688,1822,1640],{"class":694},[688,1824,1643],{"class":697},[688,1826,1784],{"class":697},[688,1828,1800],{"class":697},[13,1830,1831],{},[71,1832,1833],{},"4. 查看代理配置",[13,1835,1836],{},"如果你需要查询当前代理配置，可以使用以下命令：",[679,1838,1840],{"className":681,"code":1839,"language":684,"meta":502,"style":502},"# 查看http代理\ngit config --global --get  http.proxy\n# 查看https代理\ngit config --global --get  https.proxy\n",[42,1841,1842,1847,1861,1866],{"__ignoreMap":502},[688,1843,1844],{"class":690,"line":691},[688,1845,1846],{"class":859},"# 查看http代理\n",[688,1848,1849,1851,1853,1855,1858],{"class":690,"line":506},[688,1850,1640],{"class":694},[688,1852,1643],{"class":697},[688,1854,1646],{"class":697},[688,1856,1857],{"class":697}," --get",[688,1859,1860],{"class":697},"  http.proxy\n",[688,1862,1863],{"class":690,"line":535},[688,1864,1865],{"class":859},"# 查看https代理\n",[688,1867,1868,1870,1872,1874,1876],{"class":690,"line":609},[688,1869,1640],{"class":694},[688,1871,1643],{"class":697},[688,1873,1646],{"class":697},[688,1875,1857],{"class":697},[688,1877,1878],{"class":697},"  https.proxy\n",[13,1880,1803],{},[679,1882,1884],{"className":681,"code":1883,"language":684,"meta":502,"style":502},"# 查看http代理\ngit config  --get  http.proxy\n# 查看https代理\ngit config  --get  https.proxy\n",[42,1885,1886,1890,1901,1905],{"__ignoreMap":502},[688,1887,1888],{"class":690,"line":691},[688,1889,1846],{"class":859},[688,1891,1892,1894,1896,1899],{"class":690,"line":506},[688,1893,1640],{"class":694},[688,1895,1643],{"class":697},[688,1897,1898],{"class":697},"  --get",[688,1900,1860],{"class":697},[688,1902,1903],{"class":690,"line":535},[688,1904,1865],{"class":859},[688,1906,1907,1909,1911,1913],{"class":690,"line":609},[688,1908,1640],{"class":694},[688,1910,1643],{"class":697},[688,1912,1898],{"class":697},[688,1914,1878],{"class":697},[13,1916,1917],{},"如果VPN代理IP和端口发生改变，需要重新配置git代码地址。",[968,1919,1920],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}",{"title":502,"searchDepth":535,"depth":535,"links":1922},[1923,1928],{"id":1040,"depth":506,"text":1041,"children":1924},[1925,1926,1927],{"id":1050,"depth":535,"text":1051},{"id":1097,"depth":535,"text":1098},{"id":1499,"depth":535,"text":1500},{"id":1595,"depth":506,"text":1596,"children":1929},[1930,1931],{"id":1602,"depth":535,"text":1603},{"id":1616,"depth":535,"text":1617},{"name":1933,"symbol":1934,"icon":1935,"id":905},"GitHub","github","mdi:github","2025-06-29 15:36:09","GitHub对中国大陆IP的访问进行了限制，国内想访问GitHub站点，需要科学上网，才能访问Github站点。有时候就算通过梯子，使用git命令克隆、推送、拉取GitHub仓库的代码也会报443连接超时错误.",{"type":10,"value":1939},[1940],[13,1941,1942],{},"在国内由于网络原因，在命令窗口能够Ping通github.com域名，但通过git命令操作github仓库代码时，无论是克隆，拉取、推送还是提交，经常会出现fatal: unable to access 'xxx': Failed to connect to github.com port 443: Timed out 禁止访问连接或者github连接超时异常，有时候即使开了梯子也没用，把VPN工具改成全局路由模式也不行。下面记录几种常见解决方法，彻底解决git命令连接github连接超时问题。",{},"\u002Fblog\u002Fgithub443","---\ntitle: GitHub克隆拉取和推送代码时443超时问题解决方法\ndescription: GitHub对中国大陆IP的访问进行了限制，国内想访问GitHub站点，需要科学上网，才能访问Github站点。有时候就算通过梯子，使用git命令克隆、推送、拉取GitHub仓库的代码也会报443连接超时错误.\narticleId: github-timeout\ncreateDate: \"2025-06-29 15:36:09\"\nupdateDate: \"2026-03-12 10:40:16\"\narticleThumb: https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327204841213.webp\ntagIds: \n    - 11\ncategoryId: 11\nisTop: false\nisRecommend: false\nisHot: true\nisHidden: false\nnavigation: true\n---\n\n在国内由于网络原因，在命令窗口能够Ping通github.com域名，但通过git命令操作github仓库代码时，无论是克隆，拉取、推送还是提交，经常会出现fatal: unable to access 'xxx': Failed to connect to github.com port 443: Timed out 禁止访问连接或者github连接超时异常，有时候即使开了梯子也没用，把VPN工具改成全局路由模式也不行。下面记录几种常见解决方法，彻底解决git命令连接github连接超时问题。\n\u003C!--more-->\n\n## 1 修改hosts文件\nHosts文件是系统中用于将网址域名与IP地址进行快速映射的数据库，在用户访问网站时优先被查询。这个没有扩展名的系统文件，虽然不起眼，却扮演着至关重要的角色。它类似于一个关联“数据库”，将众多常用的网址域名与对应的IP地址紧密相连。每当用户在浏览器中输入一个网址并尝试登录时，系统会首先自动在Hosts文件中寻找相应的IP地址。一旦找到，系统会迅速打开对应的网页；若未找到，则会自动将网址提交至DNS域名解析服务器进行IP地址的深入解析。\n\n在解决 GitHub 在提交代码时出现 443 错误的问题时，修改 hosts 文件是一个可能的解决方案。这通常发生在 GitHub 的 IP 地址被错误地解析或者 DNS 解析出现问题的情况下，导致连接超时问题。通过修改hosts文件后，你可以绕过国内的访问限制，当尝试访问github及其相关域名时，系统将优先使用这些映射，从而可能解决网络连通性问题。以下是具体步骤：\n\n### 1.1 查找GitHub的IP地址\n首先，你需要找到 GitHub 的 IP 地址。可以通过运行以下命令来获取 GitHub 的 IP 地址：\n```Bash\nping github.com\n```\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250629165553736.webp)\n\n或者，你可以使用以下命令获取更多 IP 地址：\n```Bash\nnslookup github.com\n```\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F06\u002F20250629165553775.webp)\n\n### 1.2 手动编辑hosts文件\n在不同操作系统中，Hosts文件的位置各有不同，便于管理和编辑。例如，在Windows系统中，它通常位于`C:\\Windows\\System32\\drivers\\etc\\hosts`{color=primary}路径下；而在Android和iOS系统中，它则分别位于`\u002Fetc\u002Fhosts和\u002Fprivate\u002Fetc\u002Fhosts`{color=primary}等路径。对于Mac OS X系统，其位置则可能因版本不同而有所差异。\n\n- **对于Windows系统** \n\n1、**以管理员权限身份**，通过记事本打开`C:\\Windows\\System32\\drivers\\etc\\hosts`{color=primary}文件。  \n2、在文件末尾添加以下行（替换 \u003Cgithub_ip> 为你从 ping 或 nslookup 命令中获取的 IP 地址）:\n ```bash\n  \u003Cgithub_ip> github.com\n ``` \n3、保存修改后的配置文件，并关闭文件。  \n4、为了确保更改生效，你可以清空 DNS 缓存，在终端刷新本地DNS缓存：\n ```bash\nipconfig \u002Fflushdns\n ``` \n- **对于 macOS 或 Linux**\n\n1、打开终端。  \n2、使用文本编辑器打开 \u002Fetc\u002Fhosts 文件，例如使用命令：\n```bash\n sudo vi \u002Fetc\u002Fhosts\n```\n3、在文件末尾添加以下行（同样替换 \u003Cgithub_ip>）：\n```bash\n \u003Cgithub_ip> github.com\n```\n4、保存修改后的配置文件，并关闭文件。  \n5、在 macOS 或 Linux 上，通常不需要手动清空 DNS 缓存，因为系统会自动处理。但如果你怀疑有缓存问题，可以尝试重启你的计算机。\n::callout{icon=\"circum:warning\" color=\"primary\"}\n温馨提示\u003C\u002Fbr>\n1 修改hosts方法可能不太稳定，但至少在一定程度上能够解决问题。如果过一段时间后，发现修改hosts也无法连接，需要重新修改hosts文件中ip和github映射关系。\u003C\u002Fbr>\n2 如果`hosts`文件中加入`\u003Cgithub ip> github.com`后发现还是连接不通，可以尝试加入以下IP地址和域名映射关系：\n```bash\n185.199.108.154   github.githubassets.com\n140.82.113.22     central.github.com\n185.199.108.133   desktop.githubusercontent.com\n185.199.109.153   assets-cdn.github.com\n185.199.110.133   camo.githubusercontent.com\n185.199.108.133   github.map.fastly.net\n151.101.201.194   github.global.ssl.fastly.net\n140.82.116.3      gist.github.com\n185.199.108.153   github.io\n140.82.116.3      github.com\n140.82.116.6      api.github.com\n185.199.111.133   raw.githubusercontent.com\n185.199.108.133   user-images.githubusercontent.com\n185.199.108.133   favicons.githubusercontent.com\n185.199.110.133   avatars5.githubusercontent.com\n185.199.108.133   avatars4.githubusercontent.com\n185.199.108.133   avatars3.githubusercontent.com\n185.199.109.133   avatars2.githubusercontent.com\n185.199.108.133   avatars1.githubusercontent.com\n185.199.108.133   avatars0.githubusercontent.com\n185.199.109.133   avatars.githubusercontent.com\n140.82.116.9      codeload.github.com\n52.217.135.153    github-cloud.s3.amazonaws.com\n3.5.21.109        github-com.s3.amazonaws.com\n3.5.29.177        github-production-release-asset-2e65be.s3.amazonaws.com\n52.216.63.25      github-production-user-asset-6210df.s3.amazonaws.com\n52.217.223.1      github-production-repository-file-5c1aeb.s3.amazonaws.com\n185.199.110.153   githubstatus.com\n140.82.112.17     github.community\n185.199.108.133   media.githubusercontent.com\n```\n上面具体域名对应的映射ip，可以通过命令行ping的方式获取。\n::\n\n### 1.3 工具编辑hosts文件\n\n\n对于windows系统可以借助第三方工具`WinHostsManager`进行修改`hosts`文件。‌ WinHostsManager 是一款功能强大的hosts文件管理工具，主要用于管理和修改hosts文件。‌\n\n\nWinHostsManager的主要功能包括：\n\n\n1. **添加新的host项目‌：** 用户可以轻松添加新的IP地址和域名映射。\n2. **保存修改‌：** 将新的IP地址和域名直接加载到hosts文件中并保存\n3. **显示最高映射方案‌：** 提供当前最高的映射方案，方便用户选择。\n4. **获取最快的主机IP编辑方式‌：** 优化编辑过程，提高编辑效率。\n5. **支持重启主机‌：** 编辑完成后，支持重启主机以应用更改\n6. ‌**查看端口项目‌：** 在列表上查看打开的端口项目。\n7. ‌**建立过滤‌：** 提供过滤功能，方便管理hosts文件‌。\n8. **支持禁用ip：**  提供启用和禁用ip和域名映射关系功能。\n\n\n此外，WinHostsManager还具有以下特点：\n\n\n- **操作简单‌：** 用户无需知道hosts文件的保存位置，即可进行修改。\n- **节省时间‌：** 软件会自动将内容写入hosts文件，节省了在C盘上查找hosts文件地址的时间。\n- **支持特殊地址访问‌：** 帮助用户访问一些特殊地址。\n- **备份和恢复功能‌：** 支持hosts文件的备份和恢复，确保数据安全‌。\n\n## 2 配置GIT代理\n\n\n通常我们访问GitHub站点都需要本地打开VPN工具才能访问，但即使打开VPN工具，打开了全局代理模式，git拉取或者提交代码依然报443连接错误。在Git中配置代理服务器是为了帮助你通过一个HTTP或HTTPS代理连接到GitHub外部网络资源，根据你的网络环境，你可能需要设置代理来绕过某些限制或提高访问速度，才能让git客户端通过VPN代理渠道访问github，否则git客户端不走VPN代理就会出现连接错误。\n\n\n### 2.1 VPN代理IP和端口\n\n\n打开本地VPN工具，查看设置页面http(s)代理IP和端口。以海豚湾为例，查看方法如下图：\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250701092746871.webp)\n或者查看电脑本地代理端口，查看方法如下图：\n![](https:\u002F\u002Fimage.anyfork.top\u002F2025\u002F07\u002F20250701092752202.webp)\n\n\n### 2.2 GIT本地代理配置\n**1. 全局代理配置**\n\n\n如果你希望对所有Git操作使用同一个代理服务器，你可以通过以下命令格式配置全局代理：\n```bash\n# 配置http代理\ngit config --global http.proxy http:\u002F\u002Fproxy_address:port\n# 配置https代理\ngit config --global https.proxy https:\u002F\u002Fproxy_address:port\n```\n将`proxy_address`和`port`替换为你的代理服务器的地址和端口。例如：您正在使用127.0.0.1的代理服务器和4780的端口，那么您需要输入以下命令：\n```bash\n# 配置http代理\ngit config --global http.proxy http:\u002F\u002F127.0.0.1:4780\n# 配置https代理\ngit config --global https.proxy https:\u002F\u002F127.0.0.1:4780\n```\n**2. 单个仓库代理配置**\n\n\n如果你只想为特定的Git仓库设置代理，而不是全局，你需要进入到本地具体代码仓库目录下，使用以下命令：\n```bash\ngit config http.proxy http:\u002F\u002Fproxy_address:port\ngit config https.proxy https:\u002F\u002Fproxy_address:port\n```\n同样地，替换`proxy_address`和`port`为你的代理服务器信息。\n\n\n**3. 取消代理配置**\n\n\n如果你需要取消之前设置的代理配置，可以使用以下命令：\n```bash\ngit config --global --unset http.proxy\ngit config --global --unset https.proxy\n```\n或者针对单个仓库：\n```bash\ngit config --unset http.proxy\ngit config --unset https.proxy\n```\n**4. 查看代理配置**\n\n\n如果你需要查询当前代理配置，可以使用以下命令：\n```bash\n# 查看http代理\ngit config --global --get  http.proxy\n# 查看https代理\ngit config --global --get  https.proxy\n```\n或者针对单个仓库：\n```bash\n# 查看http代理\ngit config  --get  http.proxy\n# 查看https代理\ngit config  --get  https.proxy\n```\n如果VPN代理IP和端口发生改变，需要重新配置git代码地址。\n\n\n\n\n\n\n\n",10.9,{"title":1033,"description":1937},{"loc":1944,"images":1949},[1950,1951,1952,1953],{"loc":1074},{"loc":1094},{"loc":1609},{"loc":1613},"blog\u002F6.GitHub克隆拉取和推送代码时443超时问题解决方法",[905],[1957],{"name":1958,"symbol":1640,"icon":1959,"id":905},"Git","material-icon-theme:git","2026-03-12 10:40:16",1961,"M9DyDR2XFJKH2Q6wgeTWRfcunplG33vJn14-PamKb2E",{"id":1964,"title":1965,"articleId":1966,"articleThumb":1967,"body":1968,"categoryId":896,"categoryInfo":3010,"createDate":3012,"description":3013,"excerpt":3014,"extension":1001,"isHidden":1002,"isHot":1003,"isRecommend":1003,"isTop":1002,"meta":3019,"navigation":1003,"path":3020,"rawbody":3021,"readingTime":3022,"seo":3023,"sitemap":3024,"stem":3025,"tagIds":3026,"tagInfo":3027,"updateDate":3031,"wordCount":3032,"__hash__":3033},"blog\u002Fblog\u002F7.Windows和Linux系统上NVM安装多个版本的Node.js.md","Windows和Linux系统上NVM安装多个版本的Node.js","nvm-node","https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327205225806.webp",{"type":10,"value":1969,"toc":2996},[1970,1984,1988,2003,2053,2057,2079,2084,2091,2099,2102,2111,2116,2136,2141,2144,2158,2161,2165,2179,2184,2189,2215,2220,2243,2248,2251,2347,2356,2361,2377,2382,2396,2399,2403,2412,2416,2421,2517,2522,2531,2534,2572,2578,2583,2622,2627,2643,2647,2652,2668,2677,2682,2697,2702,2803,2808,2821,2825,2829,2834,2845,2850,2858,2862,2867,2875,2889,2894,2903,2961,2980,2993],[1221,1971,1974],{"color":116,"icon":1972,"target":28,"to":1973},"material-symbols:info-outline","https:\u002F\u002Fgithub.com\u002Fnvm-sh\u002Fnvm\u002Fblob\u002Fmaster\u002FREADME.md",[13,1975,1976,1977,1980,1981,1983],{},"点击访问",[42,1978,1979],{},"nvm","官网，阅读",[42,1982,1979],{},"使用文档",[17,1985,1987],{"id":1986},"_1-安装nvm软件","1. 安装NVM软件",[13,1989,1990,1991,1996,1997,1999,2000,2002],{},"访问",[104,1992,1995],{"href":1993,"rel":1994},"https:\u002F\u002Fgithub.com\u002Fnvm-sh\u002Fnvm",[108],"NVM","的源码仓库，可以查看最新版本的",[42,1998,1979],{},"包，在不同的系统上安装",[42,2001,1979],{},"方式存在差异，但基本都可以通过源代码包和第三方快捷方式进行安装。",[2004,2005,2006,2014],"warning",{},[13,2007,2008,2011,2012],{},[71,2009,2010],{},"注意："," ",[290,2013],{},[65,2015,2016,2026],{},[68,2017,2018,2019,2021,2022,2025],{},"无论什么系统，通过",[42,2020,1979],{},"安装node.js之前，都需要卸载现已安装好的",[42,2023,2024],{},"Node.js","，避免发生安装冲突和错误问题。",[68,2027,2028,2029,2032,2033,2035,2036,2039,2040,2042,2043,2045,2046,2049,2050,2052],{},"对于",[42,2030,2031],{},"Linux","系统，不同版本的",[42,2034,2024],{},"对",[42,2037,2038],{},"gcc","版本有要求，安装",[42,2041,2024],{},"对应的",[42,2044,2038],{},"版本，Node.js才能安装成功。建议使用最新版本的",[42,2047,2048],{},"linux","镜像系统(gcc版本比较高，满足高版本Node.js安装需求)，否则需要手动升级系统",[42,2051,2038],{},"版本，非常麻烦。",[123,2054,2056],{"id":2055},"_11-windows安装nvm","1.1 Windows安装NVM",[13,2058,2059,2060,2063,2064,2067,2068,2071,2072,2074,2075,2078],{},"windows系统可以通过",[42,2061,2062],{},"nvm-windows​","进行安装，",[42,2065,2066],{},"nvm-windows","是一个",[42,2069,2070],{},".exe","的安装包，在",[42,2073,1979],{},"源码包的基础上编译成了可执行文件，便于在",[42,2076,2077],{},"Windows","环境下安装。",[13,2080,2081],{},[71,2082,2083],{},"1. 下载nvm-windows",[13,2085,1990,2086,2090],{},[104,2087,2066],{"href":2088,"rel":2089},"https:\u002F\u002Fgithub.com\u002Fcoreybutler\u002Fnvm-windows\u002Freleases",[108],"官方仓库，下载最新版本的安装包。",[128,2092,2093,2096],{},[68,2094,2095],{},"稳定版：nvm-setup.exe",[68,2097,2098],{},"便携版：nvm-noinstall.zip",[13,2100,2101],{},"稳定版是可执行文件，可以直接点击安装，推荐使用稳定版安装。便携版为免安装的文件包，之间解压到磁盘上即可。",[2103,2104,2105],"tip",{},[13,2106,2107,2108,2110],{},"二者区别：稳定版安装后，会自动添加环境变量，软件安装完毕就可以直接使用",[42,2109,1979],{},",便携版需要手动配置环境变量，才能使用。",[13,2112,2113],{},[71,2114,2115],{},"2. 运行安装程序",[65,2117,2118,2121,2127,2133],{},[68,2119,2120],{},"点击运行稳定版，勾选许可协议，下一步.",[68,2122,2123,2124,2126],{},"设置",[42,2125,1979],{},"安装路径，可自定义安装路径 .",[68,2128,2129,2130,2132],{},"设置 ",[42,2131,2024],{}," 的安装路径.",[68,2134,2135],{},"安装完成.",[13,2137,2138],{},[71,2139,2140],{},"3.验证安装",[13,2142,2143],{},"打开新的命令提示符（CMD）或 PowerShell，运行：",[679,2145,2149],{"className":2146,"code":2147,"language":2148,"meta":502,"style":502},"language-shell shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","nvm -v\n","shell",[42,2150,2151],{"__ignoreMap":502},[688,2152,2153,2155],{"class":690,"line":691},[688,2154,1979],{"class":694},[688,2156,2157],{"class":697}," -v\n",[13,2159,2160],{},"如果显示版本号（如 1.1.9），说明安装成功。",[123,2162,2164],{"id":2163},"_12-linux安装nvm","1.2 Linux安装NVM",[13,2166,2167,2168,2170,2171,2174,2175,2178],{},"Linux系统上安装",[42,2169,1979],{},"，可以通过",[42,2172,2173],{},"nvm-sh","脚本和",[42,2176,2177],{},"源码包","2种方式进行安装。",[13,2180,2181],{},[71,2182,2183],{},"1. 使用脚本安装",[128,2185,2186],{},[68,2187,2188],{},"打开终端，运行以下命令：",[679,2190,2192],{"className":766,"code":2191,"language":769,"meta":502,"style":502},"curl -o- https:\u002F\u002Fraw.githubusercontent.com\u002Fnvm-sh\u002Fnvm\u002Fv0.40.3\u002Finstall.sh | bash\n",[42,2193,2194],{"__ignoreMap":502},[688,2195,2196,2199,2202,2205,2207,2210,2212],{"class":690,"line":691},[688,2197,2198],{"class":787},"curl ",[688,2200,2201],{"class":791},"-",[688,2203,2204],{"class":787},"o",[688,2206,2201],{"class":791},[688,2208,2209],{"class":694}," https",[688,2211,801],{"class":791},[688,2213,2214],{"class":859},"\u002F\u002Fraw.githubusercontent.com\u002Fnvm-sh\u002Fnvm\u002Fv0.40.3\u002Finstall.sh | bash\n",[128,2216,2217],{},[68,2218,2219],{},"或者使用 wget：",[679,2221,2223],{"className":766,"code":2222,"language":769,"meta":502,"style":502},"wget -qO- https:\u002F\u002Fraw.githubusercontent.com\u002Fnvm-sh\u002Fnvm\u002Fv0.40.3\u002Finstall.sh | bash\n",[42,2224,2225],{"__ignoreMap":502},[688,2226,2227,2230,2232,2235,2237,2239,2241],{"class":690,"line":691},[688,2228,2229],{"class":787},"wget ",[688,2231,2201],{"class":791},[688,2233,2234],{"class":787},"qO",[688,2236,2201],{"class":791},[688,2238,2209],{"class":694},[688,2240,801],{"class":791},[688,2242,2214],{"class":859},[13,2244,2245],{},[71,2246,2247],{},"2. 配置环境变量",[13,2249,2250],{},"安装完成后，将以下内容添加到你的 shell 配置文件（ ~\u002F.bashrc、 ~\u002F.zshrc 或 ~\u002F.profile）：",[679,2252,2254],{"className":766,"code":2253,"language":769,"meta":502,"style":502},"export NVM_DIR=\"$HOME\u002F.nvm\"\n[ -s \"$NVM_DIR\u002Fnvm.sh\" ] && \\. \"$NVM_DIR\u002Fnvm.sh\"  # 加载 nvm\n[ -s \"$NVM_DIR\u002Fbash_completion\" ] && \\. \"$NVM_DIR\u002Fbash_completion\"  # 加载自动补全\n",[42,2255,2256,2276,2315],{"__ignoreMap":502},[688,2257,2258,2261,2264,2267,2270,2273],{"class":690,"line":691},[688,2259,777],{"class":2260},"spNyl",[688,2262,2263],{"class":787}," NVM_DIR",[688,2265,2266],{"class":791},"=",[688,2268,2269],{"class":791},"\"",[688,2271,2272],{"class":697},"$HOME\u002F.nvm",[688,2274,2275],{"class":791},"\"\n",[688,2277,2278,2281,2283,2286,2288,2291,2293,2296,2299,2302,2305,2308,2310,2312],{"class":690,"line":506},[688,2279,2280],{"class":787},"[ ",[688,2282,2201],{"class":791},[688,2284,2285],{"class":787},"s ",[688,2287,2269],{"class":791},[688,2289,2290],{"class":697},"$NVM_DIR\u002Fnvm.sh",[688,2292,2269],{"class":791},[688,2294,2295],{"class":787}," ] ",[688,2297,2298],{"class":791},"&&",[688,2300,2301],{"class":787}," \\",[688,2303,2304],{"class":791},".",[688,2306,2307],{"class":791}," \"",[688,2309,2290],{"class":697},[688,2311,2269],{"class":791},[688,2313,2314],{"class":787},"  # 加载 nvm\n",[688,2316,2317,2319,2321,2323,2325,2328,2330,2332,2334,2336,2338,2340,2342,2344],{"class":690,"line":535},[688,2318,2280],{"class":787},[688,2320,2201],{"class":791},[688,2322,2285],{"class":787},[688,2324,2269],{"class":791},[688,2326,2327],{"class":697},"$NVM_DIR\u002Fbash_completion",[688,2329,2269],{"class":791},[688,2331,2295],{"class":787},[688,2333,2298],{"class":791},[688,2335,2301],{"class":787},[688,2337,2304],{"class":791},[688,2339,2307],{"class":791},[688,2341,2327],{"class":697},[688,2343,2269],{"class":791},[688,2345,2346],{"class":787},"  # 加载自动补全\n",[223,2348,2349],{},[13,2350,2351,2352,2355],{},"注意: 通过脚本安装，脚本执行完，默认在",[42,2353,2354],{},"~\u002F.bashrc","中加入了上面的环境变量配置，只需要重新加载环境变量文件即可。",[13,2357,2358],{},[71,2359,2360],{},"3. 重新加载环境变量",[679,2362,2364],{"className":766,"code":2363,"language":769,"meta":502,"style":502},"source ~\u002F.bashrc\n",[42,2365,2366],{"__ignoreMap":502},[688,2367,2368,2371,2374],{"class":690,"line":691},[688,2369,2370],{"class":787},"source ",[688,2372,2373],{"class":791},"~\u002F.",[688,2375,2376],{"class":787},"bashrc\n",[13,2378,2379],{},[71,2380,2381],{},"4. 验证安装",[679,2383,2384],{"className":766,"code":2147,"language":769,"meta":502,"style":502},[42,2385,2386],{"__ignoreMap":502},[688,2387,2388,2391,2393],{"class":690,"line":691},[688,2389,2390],{"class":787},"nvm ",[688,2392,2201],{"class":791},[688,2394,2395],{"class":787},"v\n",[13,2397,2398],{},"如果显示版本号（如：0.40.3)，说明安装成功。",[17,2400,2402],{"id":2401},"_2-nvm管理nodejs","2. Nvm管理Node.js",[13,2404,2405,2406,2408,2409,2411],{},"无论Window系统，还是Linux系统，",[42,2407,1979],{},"安装成功后，安装和管理",[42,2410,2024],{},"的命令基本一致。",[123,2413,2415],{"id":2414},"_21-基本命令","2.1  基本命令",[13,2417,2418],{},[71,2419,2420],{},"1. 安装 Node.js 版本",[679,2422,2424],{"className":766,"code":2423,"language":769,"meta":502,"style":502},"# 安装最新版本的node，\"node\" 是最新版本的别名\nnvm install node \n# 安装最新的 LTS 版本\nnvm install --lts \n# 安装最新 18.x 版本\nnvm install 18 \n# 安装指定版本     \nnvm install 16.14.0  \n# 设置别名为my_alias，别名不能包含空格或者斜杠\nnvm alias my_alias v14.4.0\n",[42,2425,2426,2441,2446,2451,2462,2472,2483,2488,2502,2507],{"__ignoreMap":502},[688,2427,2428,2431,2433,2436,2438],{"class":690,"line":691},[688,2429,2430],{"class":787},"# 安装最新版本的node，",[688,2432,2269],{"class":791},[688,2434,2435],{"class":697},"node",[688,2437,2269],{"class":791},[688,2439,2440],{"class":787}," 是最新版本的别名\n",[688,2442,2443],{"class":690,"line":506},[688,2444,2445],{"class":787},"nvm install node \n",[688,2447,2448],{"class":690,"line":535},[688,2449,2450],{"class":787},"# 安装最新的 LTS 版本\n",[688,2452,2453,2456,2459],{"class":690,"line":609},[688,2454,2455],{"class":787},"nvm install ",[688,2457,2458],{"class":791},"--",[688,2460,2461],{"class":787},"lts \n",[688,2463,2464,2467,2469],{"class":690,"line":624},[688,2465,2466],{"class":787},"# 安装最新 18",[688,2468,2304],{"class":791},[688,2470,2471],{"class":787},"x 版本\n",[688,2473,2474,2476,2480],{"class":690,"line":856},[688,2475,2455],{"class":787},[688,2477,2479],{"class":2478},"sbssI","18",[688,2481,2482],{"class":787}," \n",[688,2484,2485],{"class":690,"line":863},[688,2486,2487],{"class":787},"# 安装指定版本     \n",[688,2489,2490,2492,2495,2497,2500],{"class":690,"line":874},[688,2491,2455],{"class":787},[688,2493,2494],{"class":2478},"16.14",[688,2496,2304],{"class":791},[688,2498,2499],{"class":2478},"0",[688,2501,902],{"class":787},[688,2503,2504],{"class":690,"line":890},[688,2505,2506],{"class":787},"# 设置别名为my_alias，别名不能包含空格或者斜杠\n",[688,2508,2509,2512,2514],{"class":690,"line":896},[688,2510,2511],{"class":787},"nvm alias my_alias v14",[688,2513,2304],{"class":791},[688,2515,2516],{"class":2478},"4.0\n",[13,2518,2519],{},[71,2520,2521],{},"2. 查看已安装版本",[679,2523,2525],{"className":766,"code":2524,"language":769,"meta":502,"style":502},"nvm ls\n",[42,2526,2527],{"__ignoreMap":502},[688,2528,2529],{"class":690,"line":691},[688,2530,2524],{"class":787},[13,2532,2533],{},"显示结果为：",[679,2535,2537],{"className":766,"code":2536,"language":769,"meta":502,"style":502},"    v16.14.0\n    v18.12.1\n->  v20.7.0\n",[42,2538,2539,2549,2559],{"__ignoreMap":502},[688,2540,2541,2544,2546],{"class":690,"line":691},[688,2542,2543],{"class":787},"    v16",[688,2545,2304],{"class":791},[688,2547,2548],{"class":2478},"14.0\n",[688,2550,2551,2554,2556],{"class":690,"line":506},[688,2552,2553],{"class":787},"    v18",[688,2555,2304],{"class":791},[688,2557,2558],{"class":2478},"12.1\n",[688,2560,2561,2564,2567,2569],{"class":690,"line":535},[688,2562,2563],{"class":791},"->",[688,2565,2566],{"class":787},"  v20",[688,2568,2304],{"class":791},[688,2570,2571],{"class":2478},"7.0\n",[13,2573,2574,2575,2577],{},"箭头指向为当前使用的",[42,2576,2024],{},"版本",[13,2579,2580],{},[71,2581,2582],{},"3. 切换Node.js版本",[679,2584,2586],{"className":766,"code":2585,"language":769,"meta":502,"style":502},"# 切换到 18.x\nnvm use 18\n# 切换到指定版本      \nnvm use 16.14.0  \n",[42,2587,2588,2598,2606,2611],{"__ignoreMap":502},[688,2589,2590,2593,2595],{"class":690,"line":691},[688,2591,2592],{"class":787},"# 切换到 18",[688,2594,2304],{"class":791},[688,2596,2597],{"class":787},"x\n",[688,2599,2600,2603],{"class":690,"line":506},[688,2601,2602],{"class":787},"nvm use ",[688,2604,2605],{"class":2478},"18\n",[688,2607,2608],{"class":690,"line":535},[688,2609,2610],{"class":787},"# 切换到指定版本      \n",[688,2612,2613,2615,2617,2619],{"class":690,"line":609},[688,2614,2602],{"class":787},[688,2616,2494],{"class":2478},[688,2618,2304],{"class":791},[688,2620,2621],{"class":2478},"0\n",[13,2623,2624],{},[71,2625,2626],{},"4. 卸载Node.js版本",[679,2628,2630],{"className":766,"code":2629,"language":769,"meta":502,"style":502},"nvm uninstall 16.14.0\n",[42,2631,2632],{"__ignoreMap":502},[688,2633,2634,2637,2639,2641],{"class":690,"line":691},[688,2635,2636],{"class":787},"nvm uninstall ",[688,2638,2494],{"class":2478},[688,2640,2304],{"class":791},[688,2642,2621],{"class":2478},[123,2644,2646],{"id":2645},"_22-高阶用法","2.2 高阶用法",[13,2648,2649],{},[71,2650,2651],{},"1. 设置默认版本",[679,2653,2655],{"className":766,"code":2654,"language":769,"meta":502,"style":502},"nvm alias default 18\n",[42,2656,2657],{"__ignoreMap":502},[688,2658,2659,2662,2665],{"class":690,"line":691},[688,2660,2661],{"class":787},"nvm alias ",[688,2663,2664],{"class":776},"default",[688,2666,2667],{"class":2478}," 18\n",[13,2669,2670,2671,2673,2674,2676],{},"第一个安装的版本会被自动设置为默认版本，新打开的终端",[42,2672,2148],{},"都会使用默认的",[42,2675,2024],{},"版本。",[13,2678,2679],{},[71,2680,2681],{},"2. 查看远程可用版本",[679,2683,2685],{"className":766,"code":2684,"language":769,"meta":502,"style":502},"nvm ls-remote\n",[42,2686,2687],{"__ignoreMap":502},[688,2688,2689,2692,2694],{"class":690,"line":691},[688,2690,2691],{"class":787},"nvm ls",[688,2693,2201],{"class":791},[688,2695,2696],{"class":787},"remote\n",[13,2698,2699],{},[71,2700,2701],{},"3. 临时选择版本，并输出版本号，该命令不会永久切换版本，仅临时运行一次",[679,2703,2705],{"className":766,"code":2704,"language":769,"meta":502,"style":502},"# 临时选择最新node版本，并输出版本号\nnvm run node --version\n# 临时切换到 Node.js 18.12.1 版本，并输出该版本的 Node.js 号\nnvm run 18.12.1 node --version\n# 临时切换到 Node.js 18.12.1 版本，并输出该版本的 Node.js 号\nnvm exec 18.12.1 node --version\n",[42,2706,2707,2712,2722,2748,2766,2786],{"__ignoreMap":502},[688,2708,2709],{"class":690,"line":691},[688,2710,2711],{"class":787},"# 临时选择最新node版本，并输出版本号\n",[688,2713,2714,2717,2719],{"class":690,"line":506},[688,2715,2716],{"class":787},"nvm run node ",[688,2718,2458],{"class":791},[688,2720,2721],{"class":787},"version\n",[688,2723,2724,2727,2729,2732,2735,2737,2740,2743,2745],{"class":690,"line":535},[688,2725,2726],{"class":787},"# 临时切换到 Node",[688,2728,2304],{"class":791},[688,2730,2731],{"class":787},"js ",[688,2733,2734],{"class":2478},"18.12",[688,2736,2304],{"class":791},[688,2738,2739],{"class":2478},"1",[688,2741,2742],{"class":787}," 版本，并输出该版本的 Node",[688,2744,2304],{"class":791},[688,2746,2747],{"class":787},"js 号\n",[688,2749,2750,2753,2755,2757,2759,2762,2764],{"class":690,"line":609},[688,2751,2752],{"class":787},"nvm run ",[688,2754,2734],{"class":2478},[688,2756,2304],{"class":791},[688,2758,2739],{"class":2478},[688,2760,2761],{"class":787}," node ",[688,2763,2458],{"class":791},[688,2765,2721],{"class":787},[688,2767,2768,2770,2772,2774,2776,2778,2780,2782,2784],{"class":690,"line":624},[688,2769,2726],{"class":787},[688,2771,2304],{"class":791},[688,2773,2731],{"class":787},[688,2775,2734],{"class":2478},[688,2777,2304],{"class":791},[688,2779,2739],{"class":2478},[688,2781,2742],{"class":787},[688,2783,2304],{"class":791},[688,2785,2747],{"class":787},[688,2787,2788,2791,2793,2795,2797,2799,2801],{"class":690,"line":856},[688,2789,2790],{"class":787},"nvm exec ",[688,2792,2734],{"class":2478},[688,2794,2304],{"class":791},[688,2796,2739],{"class":2478},[688,2798,2761],{"class":787},[688,2800,2458],{"class":791},[688,2802,2721],{"class":787},[13,2804,2805],{},[71,2806,2807],{},"4. 查看已安装Node.js的安装路径",[679,2809,2811],{"className":766,"code":2810,"language":769,"meta":502,"style":502},"nvm which 12.22\n",[42,2812,2813],{"__ignoreMap":502},[688,2814,2815,2818],{"class":690,"line":691},[688,2816,2817],{"class":787},"nvm which ",[688,2819,2820],{"class":2478},"12.22\n",[17,2822,2824],{"id":2823},"_3-常见问题","3. 常见问题",[123,2826,2828],{"id":2827},"_31-windows-常见问题","3.1. Windows 常见问题",[13,2830,2831],{},[71,2832,2833],{},"1. nvm 命令无效​",[128,2835,2836,2839,2842],{},[68,2837,2838],{},"确保以管理员身份运行命令提示符。",[68,2840,2841],{},"检查环境变量是否设置正确。",[68,2843,2844],{},"关闭终端重新打开或者重启电脑。",[13,2846,2847],{},[71,2848,2849],{},"2. 安装失败",[128,2851,2852,2855],{},[68,2853,2854],{},"安装路径不能有中文和空格。",[68,2856,2857],{},"关闭杀毒软件。",[123,2859,2861],{"id":2860},"_32-linux-常见问题","3.2. Linux 常见问题",[13,2863,2864],{},[71,2865,2866],{},"1. nvm命令找不到",[128,2868,2869,2872],{},[68,2870,2871],{},"nvm环境变量是否配置成功。",[68,2873,2874],{},"环境变量是否重新加载。",[13,2876,2877,2878,2881,2882,2885,2886],{},"运行",[42,2879,2880],{"color":116}," source ~\u002F.bashrc"," 或",[42,2883,2884],{"color":116}," source ~\u002F.zshrc","  或者",[42,2887,2888],{"color":116},"source ~\u002F.profile",[13,2890,2891],{},[71,2892,2893],{},"2. Node.js安装速度慢",[13,2895,2896,2897,2899,2900,2902],{},"在",[42,2898,2031],{},"系统环境变量中配置",[42,2901,2024],{},"阿里云镜像地址",[679,2904,2906],{"className":681,"code":2905,"language":684,"meta":502,"style":502},"# 编辑环境变量文件\nvi ~\u002F.bashrc\n# 最后一行输入\nexport NVM_NODEJS_ORG_MIRROR=https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n# 保存退出\n:wq!\n# 重新加载环境变量\nsource  ~\u002F.bashrc\n",[42,2907,2908,2913,2921,2926,2938,2943,2948,2953],{"__ignoreMap":502},[688,2909,2910],{"class":690,"line":691},[688,2911,2912],{"class":859},"# 编辑环境变量文件\n",[688,2914,2915,2918],{"class":690,"line":506},[688,2916,2917],{"class":694},"vi",[688,2919,2920],{"class":697}," ~\u002F.bashrc\n",[688,2922,2923],{"class":690,"line":535},[688,2924,2925],{"class":859},"# 最后一行输入\n",[688,2927,2928,2930,2933,2935],{"class":690,"line":609},[688,2929,777],{"class":2260},[688,2931,2932],{"class":787}," NVM_NODEJS_ORG_MIRROR",[688,2934,2266],{"class":791},[688,2936,2937],{"class":787},"https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n",[688,2939,2940],{"class":690,"line":624},[688,2941,2942],{"class":859},"# 保存退出\n",[688,2944,2945],{"class":690,"line":856},[688,2946,2947],{"class":694},":wq!\n",[688,2949,2950],{"class":690,"line":863},[688,2951,2952],{"class":859},"# 重新加载环境变量\n",[688,2954,2955,2958],{"class":690,"line":874},[688,2956,2957],{"class":783},"source",[688,2959,2960],{"class":697},"  ~\u002F.bashrc\n",[13,2962,2896,2963,2966,2967,2969,2970,2972,2973,2975,2976,2979],{},[42,2964,2965],{},"windows","系统中配置",[42,2968,1995],{},"下载",[42,2971,2024],{},"使用的阿里云镜像地址，在",[42,2974,1979],{},"安装目录下的",[42,2977,2978],{},"settings.txt","文件中添加以下内容：",[679,2981,2983],{"className":681,"code":2982,"language":684,"meta":502,"style":502},"node_mirror: https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n\n",[42,2984,2985],{"__ignoreMap":502},[688,2986,2987,2990],{"class":690,"line":691},[688,2988,2989],{"class":694},"node_mirror:",[688,2991,2992],{"class":697}," https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n",[968,2994,2995],{},"html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}",{"title":502,"searchDepth":535,"depth":535,"links":2997},[2998,3002,3006],{"id":1986,"depth":506,"text":1987,"children":2999},[3000,3001],{"id":2055,"depth":535,"text":2056},{"id":2163,"depth":535,"text":2164},{"id":2401,"depth":506,"text":2402,"children":3003},[3004,3005],{"id":2414,"depth":535,"text":2415},{"id":2645,"depth":535,"text":2646},{"id":2823,"depth":506,"text":2824,"children":3007},[3008,3009],{"id":2827,"depth":535,"text":2828},{"id":2860,"depth":535,"text":2861},{"name":2024,"symbol":2435,"icon":3011,"id":896},"material-icon-theme:nodejs-alt","2025-06-16 21:33:26","在Windows和Linux系统上，通过NVM版本管理器安装和管理不同版本的Node.js，易于快速实现不同版本的Nose.js安装和切换，满足不同应用场景的需求，同时也降低运维成本。",{"type":10,"value":3015},[3016],[13,3017,3018],{},"NVM（Node Version Manager）是专为Node.js设计的版本管理工具，用于安装和管理多个不同版本的Node.js，允许在同一台机器上安装、切换和使用多个不同版本的Node.js，适用于开发者处理多个项目需要不同Node版本的需求。本文从Windows和Linux2个不同的操作系统入手，简单记录NVM安装不同Node.js的使用步骤。",{},"\u002Fblog\u002Fwindowslinuxnvmnode.js","---\ntitle: Windows和Linux系统上NVM安装多个版本的Node.js\ndescription: 在Windows和Linux系统上，通过NVM版本管理器安装和管理不同版本的Node.js，易于快速实现不同版本的Nose.js安装和切换，满足不同应用场景的需求，同时也降低运维成本。\narticleId: nvm-node\ncreateDate: \"2025-06-16 21:33:26\"\nupdateDate: \"2026-03-12 10:44:16\"\narticleThumb: https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260327205225806.webp\ntagIds: \n    - 10\ncategoryId: 10\nisTop: false\nisRecommend: true\nisHot: true\nisHidden: false\nnavigation: true\n---\nNVM（Node Version Manager）是专为Node.js设计的版本管理工具，用于安装和管理多个不同版本的Node.js，允许在同一台机器上安装、切换和使用多个不同版本的Node.js，适用于开发者处理多个项目需要不同Node版本的需求。本文从Windows和Linux2个不同的操作系统入手，简单记录NVM安装不同Node.js的使用步骤。\n\u003C!--more-->\n\n::callout{icon=\"material-symbols:info-outline\" color=\"primary\" to=\"https:\u002F\u002Fgithub.com\u002Fnvm-sh\u002Fnvm\u002Fblob\u002Fmaster\u002FREADME.md\" target=\"_blank\"}\n点击访问`nvm`官网，阅读`nvm`使用文档\n::\n\n## 1. 安装NVM软件\n访问[NVM](https:\u002F\u002Fgithub.com\u002Fnvm-sh\u002Fnvm)的源码仓库，可以查看最新版本的`nvm`包，在不同的系统上安装`nvm`方式存在差异，但基本都可以通过源代码包和第三方快捷方式进行安装。\n::warning\n**注意：** \u003C\u002Fbr>\n1. 无论什么系统，通过`nvm`安装node.js之前，都需要卸载现已安装好的`Node.js`，避免发生安装冲突和错误问题。\n2. 对于`Linux`系统，不同版本的`Node.js`对`gcc`版本有要求，安装`Node.js`对应的`gcc`版本，Node.js才能安装成功。建议使用最新版本的`linux`镜像系统(gcc版本比较高，满足高版本Node.js安装需求)，否则需要手动升级系统`gcc`版本，非常麻烦。\n::\n\n### 1.1 Windows安装NVM\nwindows系统可以通过`nvm-windows​`进行安装，`nvm-windows`是一个`.exe`的安装包，在`nvm`源码包的基础上编译成了可执行文件，便于在`Windows`环境下安装。\n\n**1. 下载nvm-windows**\n\n访问[nvm-windows](https:\u002F\u002Fgithub.com\u002Fcoreybutler\u002Fnvm-windows\u002Freleases)官方仓库，下载最新版本的安装包。\n- 稳定版：nvm-setup.exe\n- 便携版：nvm-noinstall.zip\n\n稳定版是可执行文件，可以直接点击安装，推荐使用稳定版安装。便携版为免安装的文件包，之间解压到磁盘上即可。\n::tip\n二者区别：稳定版安装后，会自动添加环境变量，软件安装完毕就可以直接使用`nvm`,便携版需要手动配置环境变量，才能使用。\n::\n**2. 运行安装程序**\n1. 点击运行稳定版，勾选许可协议，下一步.\n2. 设置`nvm`安装路径，可自定义安装路径 .\n3. 设置 `Node.js` 的安装路径.\n4. 安装完成.\n\n**3.验证安装**\n\n打开新的命令提示符（CMD）或 PowerShell，运行：\n```shell\nnvm -v\n```\n如果显示版本号（如 1.1.9），说明安装成功。\n### 1.2 Linux安装NVM\nLinux系统上安装`nvm`，可以通过`nvm-sh`脚本和`源码包`2种方式进行安装。\n\n**1. 使用脚本安装**\n- 打开终端，运行以下命令：\n```ts\ncurl -o- https:\u002F\u002Fraw.githubusercontent.com\u002Fnvm-sh\u002Fnvm\u002Fv0.40.3\u002Finstall.sh | bash\n```\n- 或者使用 wget：\n```ts\nwget -qO- https:\u002F\u002Fraw.githubusercontent.com\u002Fnvm-sh\u002Fnvm\u002Fv0.40.3\u002Finstall.sh | bash\n```\n**2. 配置环境变量**\n\n安装完成后，将以下内容添加到你的 shell 配置文件（ ~\u002F.bashrc、 ~\u002F.zshrc 或 ~\u002F.profile）：\n```ts\nexport NVM_DIR=\"$HOME\u002F.nvm\"\n[ -s \"$NVM_DIR\u002Fnvm.sh\" ] && \\. \"$NVM_DIR\u002Fnvm.sh\"  # 加载 nvm\n[ -s \"$NVM_DIR\u002Fbash_completion\" ] && \\. \"$NVM_DIR\u002Fbash_completion\"  # 加载自动补全\n```\n::note\n注意: 通过脚本安装，脚本执行完，默认在`~\u002F.bashrc`中加入了上面的环境变量配置，只需要重新加载环境变量文件即可。\n::\n**3. 重新加载环境变量**\n```ts\nsource ~\u002F.bashrc\n```\n**4. 验证安装**\n```ts\nnvm -v\n```\n如果显示版本号（如：0.40.3)，说明安装成功。\n## 2. Nvm管理Node.js\n无论Window系统，还是Linux系统，`nvm`安装成功后，安装和管理`Node.js`的命令基本一致。\n### 2.1  基本命令\n**1. 安装 Node.js 版本**\n```ts\n# 安装最新版本的node，\"node\" 是最新版本的别名\nnvm install node \n# 安装最新的 LTS 版本\nnvm install --lts \n# 安装最新 18.x 版本\nnvm install 18 \n# 安装指定版本     \nnvm install 16.14.0  \n# 设置别名为my_alias，别名不能包含空格或者斜杠\nnvm alias my_alias v14.4.0\n```\n**2. 查看已安装版本**\n```ts\nnvm ls\n```\n显示结果为：\n```ts\n    v16.14.0\n    v18.12.1\n->  v20.7.0\n```\n箭头指向为当前使用的`Node.js`版本\n\n**3. 切换Node.js版本**\n```ts\n# 切换到 18.x\nnvm use 18\n# 切换到指定版本      \nnvm use 16.14.0  \n```\n**4. 卸载Node.js版本**\n```ts\nnvm uninstall 16.14.0\n```\n### 2.2 高阶用法\n\n**1. 设置默认版本**\n```ts\nnvm alias default 18\n```\n第一个安装的版本会被自动设置为默认版本，新打开的终端`shell`都会使用默认的`Node.js`版本。\n\n**2. 查看远程可用版本**\n```ts\nnvm ls-remote\n```\n**3. 临时选择版本，并输出版本号，该命令不会永久切换版本，仅临时运行一次**\n```ts\n# 临时选择最新node版本，并输出版本号\nnvm run node --version\n# 临时切换到 Node.js 18.12.1 版本，并输出该版本的 Node.js 号\nnvm run 18.12.1 node --version\n# 临时切换到 Node.js 18.12.1 版本，并输出该版本的 Node.js 号\nnvm exec 18.12.1 node --version\n```\n**4. 查看已安装Node.js的安装路径**\n```ts\nnvm which 12.22\n```\n## 3. 常见问题\n### 3.1. Windows 常见问题\n**1. nvm 命令无效​**\n- 确保以管理员身份运行命令提示符。\n- 检查环境变量是否设置正确。\n- 关闭终端重新打开或者重启电脑。\n\n\n**2. 安装失败**\n- 安装路径不能有中文和空格。\n- 关闭杀毒软件。\n\n\n### 3.2. Linux 常见问题\n**1. nvm命令找不到**\n- nvm环境变量是否配置成功。\n- 环境变量是否重新加载。\n\n运行` source ~\u002F.bashrc`{color=primary} 或` source ~\u002F.zshrc`{color=primary}  或者`source ~\u002F.profile`{color=primary} \n\n\n**2. Node.js安装速度慢**\n\n\n在`Linux`系统环境变量中配置`Node.js`阿里云镜像地址\n```bash\n# 编辑环境变量文件\nvi ~\u002F.bashrc\n# 最后一行输入\nexport NVM_NODEJS_ORG_MIRROR=https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n# 保存退出\n:wq!\n# 重新加载环境变量\nsource  ~\u002F.bashrc\n```\n在`windows`系统中配置`NVM`下载`Node.js`使用的阿里云镜像地址，在`nvm`安装目录下的`settings.txt`文件中添加以下内容：\n```bash\nnode_mirror: https:\u002F\u002Fnpmmirror.com\u002Fmirrors\u002Fnode\n\n```",6.9,{"title":1965,"description":3013},{"loc":3020},"blog\u002F7.Windows和Linux系统上NVM安装多个版本的Node.js",[896],[3028],{"name":3029,"icon":3030,"symbol":1979,"id":896},"Nvm","arcticons:nvm","2026-03-12 10:44:16",1249,"9iHIa12iRIYm9H-TvvIow1Llgd04AkZq0PGYBX-u5WE",{"id":3035,"title":3036,"articleId":3037,"articleThumb":3038,"body":3039,"categoryId":896,"categoryInfo":8666,"createDate":8667,"description":3047,"excerpt":8668,"extension":1001,"isHidden":1002,"isHot":1003,"isRecommend":1002,"isTop":1002,"meta":8676,"navigation":1003,"path":8677,"rawbody":8678,"readingTime":8679,"seo":8680,"sitemap":8681,"stem":8682,"tagIds":8683,"tagInfo":8684,"updateDate":8689,"wordCount":8690,"__hash__":8691},"blog\u002Fblog\u002F9.Web Crypto API浏览器原生加密的终极指南.md","Web Crypto API 浏览器原生加密的终极指南","web-crypto-api","https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260825111119031.webp",{"type":10,"value":3040,"toc":8642},[3041,3045,3048,3051,3054,3057,3060,3065,3069,3072,3080,3094,3101,3115,3123,3137,3144,3157,3164,3178,3182,3185,3199,3203,3206,3210,3213,3246,3250,3253,3309,3313,3316,3358,3362,3365,3391,3395,3398,3430,3434,3513,3517,3524,3584,3595,3602,3632,3635,3642,3712,3723,3730,3786,3789,3796,3853,3864,3868,3917,3921,3924,3928,3931,3934,3942,4307,4315,4472,4480,4778,4786,5035,5040,5044,5047,5054,5255,5262,5408,5415,5552,5556,5559,5566,5731,5738,5894,5901,6077,6081,6084,6087,6094,6203,6210,6379,6383,6390,6417,6424,6458,6465,6476,6480,6487,6490,6578,6581,6653,6659,6665,6672,6675,6686,6690,6693,6696,8180,8183,8186,8639],[17,3042,3044],{"id":3043},"_1-什么是-web-crypto-api","1. 什么是 Web Crypto API？",[13,3046,3047],{},"Web Crypto API 是浏览器原生提供的一套加密接口，允许 JavaScript 脚本使用密码学原语来构建基于加密的系统。它自 2015 年 7 月起已得到各大浏览器的广泛支持，是一个相当成熟且稳定的 Web 标准。",[13,3049,3050],{},"在 Web Crypto 规范诞生前，部分浏览器为了满足加密需求，在全局实现了一个名为 Crypto 的接口(即 window.crypto)。但正由于这样，当时各大浏览器的实现缺乏统一的 W3C 标准，定义模糊，且在密码学意义上是不够严谨和安全的（not cryptographically sound）。",[13,3052,3053],{},"当正式的 Web Crypto API 标准（支持 encrypt、decrypt、sign、verify 等现代异步加密操作）推出时，为了彻底避免和老旧、不安全的旧接口方法发生命名混淆与冲突，W3C 规范决定将这些低底层的密码学核心方法群全部打包进一个全新的独立接口中，这就是 SubtleCrypto。你可以通过全局对象的 crypto.subtle 属性来访问它。",[13,3055,3056],{},"由于旧的全局 crypto 对象并没有被废除（它依然保留了诸如密码学安全的伪随机数生成器 crypto.getRandomValues() 和 crypto.randomUUID() 等简单的同步方法），因此所有新版的 SubtleCrypto 复杂加密算法全部被挂载到了它的下级属性中。你必须通过 crypto.subtle 属性才能访问到这一整套现代加解密方法。",[13,3058,3059],{},"Node.js 最早在 v15.0.0 引入了 Web Crypto API 接口。直到 Node.js 19.0.0，官方正式去掉了实验性警告，将其标记为 稳定版本（Stable），并且默认在全局作用域挂载了 globalThis.crypto。",[2004,3061,3062],{},[13,3063,3064],{},"重要提示：在现代浏览器中，为了保障密钥安全，crypto.subtle 属性被限制为只能在安全上下文（HTTPS 协议或 localhost）下才能被成功访问。如果你在普通的非加密 HTTP 网页里调用 crypto.subtle，它会直接返回 undefined。",[17,3066,3068],{"id":3067},"_2-为什么选择-web-crypto-api","2. 为什么选择 Web Crypto API？",[13,3070,3071],{},"在决定是否在 Web 项目中使用 Web Crypto API 时，通常是因为它相比传统的第三方加密库（如 CryptoJS, jsencrypt, forge 等）在安全性、性能和原生支持度上带来了降维打击的优势。",[65,3073,3074],{},[68,3075,3076,3079],{},[71,3077,3078],{},"防 XSS 攻击的“密钥黑盒”机制（核心安全优势）"," : 这是选择Web Crypto API最关键的安全理由。",[128,3081,3082,3088],{},[68,3083,3084,3087],{},[71,3085,3086],{},"传统加密库的痛点："," 如果你用 CryptoJS，密钥必须以明文字符串或 Uint8Array 的形式存在于 JavaScript 变量、localStorage 或 sessionStorage 中。一旦网站遭受 XSS（跨站脚本）攻击，黑客可以直接读取并盗走你的密钥。",[68,3089,3090,3093],{},[71,3091,3092],{},"Web Crypto 的解决办法："," 它引入了 CryptoKey 对象。在生成或导入密钥时，如果设置 extractable: false，这个密钥在创建后将永远无法通过 JavaScript 读取、打印或导出。它被死死封锁在浏览器的底层安全内存中。黑客即使通过 XSS 控制了你的页面，也拿不到密钥本身，只能在本地调用它，大大降低了密钥泄露的风险。",[65,3095,3096],{"start":506},[68,3097,3098],{},[71,3099,3100],{},"硬件级的高性能与非阻塞（异步原生）",[128,3102,3103,3109],{},[68,3104,3105,3108],{},[71,3106,3107],{},"原生 C++ \u002F 汇编实现："," Web Crypto API 是由浏览器底层（基于 OpenSSL 或操作系统自带的密码学库）用原生代码实现的。相比纯 JavaScript 编写的加密库，它的运行速度快了数十倍到上百倍。",[68,3110,3111,3114],{},[71,3112,3113],{},"异步非阻塞："," 传统的加密库（如 CryptoJS）大都是同步执行的。当计算大文件哈希，或者进行复杂的 RSA 大素数生成时，会直接卡死浏览器的 UI 主线程，导致页面瞬间卡顿或未响应。而 Web Crypto 的所有核心方法（如 encrypt、digest）全部是异步 Promise 机制，浏览器会将其交给底层的系统线程去算，完全不影响前端界面的流畅度。",[65,3116,3117],{"start":535},[68,3118,3119,3122],{},[71,3120,3121],{},"密码学安全的伪随机数生成器 (CSPRNG)",": 在密码学中，随机数的质量决定了加密的生死。",[128,3124,3125,3131],{},[68,3126,3127,3130],{},[71,3128,3129],{},"传统的隐患："," JavaScript 原生的 Math.random() 是非密码学安全的，它的随机轨迹可以被算法预测，绝不能用于生成加密密钥、IV（初始化向量）或盐值。",[68,3132,3133,3136],{},[71,3134,3135],{},"Web Crypto 的保障："," 提供了 crypto.getRandomValues()，它直接调用操作系统底层的硬件熵源（如系统的 \u002Fdev\u002Furandom 或 CAPI），生成密码学安全的高强度真随机数，保障加密系统的根基不可被破解。",[65,3138,3139],{"start":609},[68,3140,3141],{},[71,3142,3143],{},"零依赖，极致减小打包体积 (Zero-Bundle)",[128,3145,3146,3151],{},[68,3147,3148,3150],{},[71,3149,3086],{}," 引入 CryptoJS、JSEncrypt 或 Forge 会让前端的 Vendor 打包体积（Bundle Size）平白无故增加几十 KB 甚至几百 KB，影响首屏加载速度。",[68,3152,3153,3156],{},[71,3154,3155],{},"Web Crypto 的优势："," 它是现代浏览器的标准内置 API，不需要 npm install 任何第三方包。直接开箱即用，让前端工程更加轻量、纯净。",[65,3158,3159],{"start":624},[68,3160,3161],{},[71,3162,3163],{},"行业标准规范，跨端兼容性完美",[128,3165,3166,3172],{},[68,3167,3168,3171],{},[71,3169,3170],{},"大厂和 W3C 背书："," 它遵循严格的 W3C 规范，由各大浏览器厂商共同维护和不断优化。",[68,3173,3174,3177],{},[71,3175,3176],{},"环境通用性："," 目前 Web Crypto API 的覆盖率已接近 100%。不仅在 Chrome、Safari、Edge、Firefox 浏览器中完美支持，在现代的 Node.js (v15+)、Deno、Bun 以及 Cloudflare Workers 等服务端\u002F边缘计算环境中也实现了完全一致的 API。这使得你写的一套加解密代码，可以无缝在前端和服务端之间复用。",[17,3179,3181],{"id":3180},"_3-什么时候不应该选择它","3. 什么时候不应该选择它？",[13,3183,3184],{},"使用 Web Crypto API 有唯一的限制条件：",[128,3186,3187,3193],{},[68,3188,3189,3192],{},[71,3190,3191],{},"非安全上下文限制："," 它强制要求 HTTPS 环境（本地开发支持 localhost 或 127.0.0.1）。如果你的网站部署在纯 HTTP 生产环境下，crypto.subtle 会直接返回 undefined 无法工作。",[68,3194,3195,3198],{},[71,3196,3197],{},"需要高层封装："," 它提供的是非常底层的“密码学原语”（Raw Primitives），不提供像“一键生成 JWT”或者“一键做密码信封”这样的高级功能，开发者需要自己处理字节对齐、IV 传递等微秒的细节。",[17,3200,3202],{"id":3201},"_4-web-crypto-api核心接口","4. Web Crypto API核心接口",[13,3204,3205],{},"Web Crypto API（通常称为 crypto.subtle）的核心接口主要由以下 5 个标准接口（Interfaces） 和 1 个全局入口 组成。它们构成了浏览器原生密码学生态的基石。",[123,3207,3209],{"id":3208},"_41-全局入口对象crypto","4.1 全局入口对象Crypto",[13,3211,3212],{},"虽然这不是单独的加解密接口，但它是所有 Web 加密功能的总入口，挂载在全局 window.crypto（或 globalThis.crypto）上。Crypto提供基础的加密功能，主要包括：",[128,3214,3215,3221],{},[68,3216,3217,3220],{},[71,3218,3219],{},"核心作用："," 提供基础的同步安全原语。",[68,3222,3223,3226],{},[71,3224,3225],{},"常用方法：",[128,3227,3228,3234,3240],{},[68,3229,3230,3233],{},[71,3231,3232],{},"crypto.getRandomValues()："," 生成密码学安全的伪随机数（常用于生成 IV 或盐）。",[68,3235,3236,3239],{},[71,3237,3238],{},"crypto.randomUUID()："," 快速生成一个符合 RFC 4122 标准的随机 UUID 字符串。",[68,3241,3242,3245],{},[71,3243,3244],{},"crypto.subtle："," 指向核心异步加密接口 SubtleCrypto 的指针。",[123,3247,3249],{"id":3248},"_42-核心操作员subtlecrypto","4.2  核心操作员SubtleCrypto",[13,3251,3252],{},"这是整个 Web Crypto API 中最重要、最核心的接口。所有的实际异步加解密、签名、密钥派生等操作都在这里执行。SubtleCrypto 提供的主要方法包括：",[128,3254,3255,3260],{},[68,3256,3257,3259],{},[71,3258,3219],{}," 提供一系列底层、底层的异步密码学原语方法（全部返回 Promise）。",[68,3261,3262,3265],{},[71,3263,3264],{},"核心方法（按功能分类）：",[128,3266,3267,3273,3279,3285,3291,3297,3303],{},[68,3268,3269,3272],{},[71,3270,3271],{},"加密与解密："," encrypt() \u002F decrypt()",[68,3274,3275,3278],{},[71,3276,3277],{},"签名与验证："," sign() \u002F verify()",[68,3280,3281,3284],{},[71,3282,3283],{},"哈希计算："," digest()",[68,3286,3287,3290],{},[71,3288,3289],{},"密钥生成："," generateKey()",[68,3292,3293,3296],{},[71,3294,3295],{},"密钥导入与导出："," importKey() \u002F exportKey()",[68,3298,3299,3302],{},[71,3300,3301],{},"密钥包装与解包："," wrapKey() \u002F unwrapKey()",[68,3304,3305,3308],{},[71,3306,3307],{},"密钥派生："," deriveKey() \u002F deriveBits()",[123,3310,3312],{"id":3311},"_43-密钥实体cryptokey","4.3 密钥实体CryptoKey",[13,3314,3315],{},"该接口代表一个在浏览器中生成的、受保护的密钥对象。为了安全，浏览器的 SubtleCrypto 方法不直接操作纯文本的密钥字符串（如 \"my-secret-key\"），而是操作 CryptoKey 实例。",[128,3317,3318,3323],{},[68,3319,3320,3322],{},[71,3321,3219],{}," 作为参数传递给 SubtleCrypto 的各项方法。",[68,3324,3325,3328],{},[71,3326,3327],{},"核心属性（只读）：",[128,3329,3330,3336,3342,3348],{},[68,3331,3332,3335],{},[71,3333,3334],{},"type："," 密钥类型（\"secret\" 对称加密、\"private\" 私钥、\"public\" 公钥）。",[68,3337,3338,3341],{},[71,3339,3340],{},"extractable："," 布尔值。如果为 false，该密钥在创建后绝对无法从内存中被导出或用 JS 打印出来，防止 XSS 攻击窃取密钥。",[68,3343,3344,3347],{},[71,3345,3346],{},"algorithm："," 绑定该密钥的算法对象（例如绑定的 AES-GCM 或 RSA-PSS 等参数）。",[68,3349,3350,3353,3354,3357],{},[71,3351,3352],{},"usages："," 该密钥被允许的用途数组（例如 ",[688,3355,3356],{},"'encrypt', 'decrypt'","）。",[123,3359,3361],{"id":3360},"_44-非对称密钥对cryptokeypair","4.4 非对称密钥对CryptoKeyPair",[13,3363,3364],{},"当使用 RSA、ECDSA 等非对称加密算法时，crypto.subtle.generateKey() 返回的不是单个密钥，而是一个包含公钥和私钥的 CryptoKeyPair 对象。",[128,3366,3367,3372],{},[68,3368,3369,3371],{},[71,3370,3219],{}," 容器对象，封装非对称密钥对。",[68,3373,3374,3377],{},[71,3375,3376],{},"核心属性：",[128,3378,3379,3385],{},[68,3380,3381,3384],{},[71,3382,3383],{},"publicKey："," 一个 CryptoKey 实例，代表公钥（用于加密或验证签名）。",[68,3386,3387,3390],{},[71,3388,3389],{},"privateKey："," 一个 CryptoKey 实例，代表私钥（用于解密或生成签名）。",[123,3392,3394],{"id":3393},"_45-算法配置对象algorithmidentifier","4.5 算法配置对象AlgorithmIdentifier",[13,3396,3397],{},"虽然它在技术上是一个规范定义的字典对象（Dictionary），而不是传统的类或接口，但它是每个 API 都必传的灵魂参数。",[128,3399,3400,3405],{},[68,3401,3402,3404],{},[71,3403,3219],{}," 用来指定具体的密码学算法名称以及其特有的初始化附加参数（如 IV、盐值、计数器等）。",[68,3406,3407,3410],{},[71,3408,3409],{},"常见表现形式：",[128,3411,3412,3418,3424],{},[68,3413,3414,3417],{},[71,3415,3416],{},"哈希配置："," { name: \"SHA-256\" }",[68,3419,3420,3423],{},[71,3421,3422],{},"AES-GCM 配置："," { name: \"AES-GCM\", iv: Uint8Array, tagLength: 128 }",[68,3425,3426,3429],{},[71,3427,3428],{},"PBKDF2（密码派生）配置："," { name: \"PBKDF2\", salt: Uint8Array, iterations: 100000, hash: \"SHA-256\" }",[123,3431,3433],{"id":3432},"_46-核心接口对比速查表","4.6 核心接口对比速查表",[260,3435,3436,3452],{},[263,3437,3438],{},[266,3439,3440,3443,3446,3449],{},[269,3441,3442],{},"接口名称",[269,3444,3445],{},"角色定位",[269,3447,3448],{},"非安全上下文（HTTP）使用规则",[269,3450,3451],{},"典型返回值 \u002F 用途",[278,3453,3454,3468,3486,3500],{},[266,3455,3456,3459,3462,3465],{},[283,3457,3458],{},"Crypto",[283,3460,3461],{},"全局大管家",[283,3463,3464],{},"是（部分方法受限）",[283,3466,3467],{},"提供crypto.subtle入口、用于生成UUID",[266,3469,3470,3473,3476,3479],{},[283,3471,3472],{},"SubtleCrypto",[283,3474,3475],{},"核心工具库",[283,3477,3478],{},"否（必须在HTTPS\u002Flocalhost环境下使用）",[283,3480,3481,3482,3485],{},"返回",[42,3483,3484],{},"Promise\u003CArrayBuffer>","，用于生成密文、哈希等加密核心操作",[266,3487,3488,3491,3494,3497],{},[283,3489,3490],{},"CryptoKey",[283,3492,3493],{},"密钥黑盒",[283,3495,3496],{},"否",[283,3498,3499],{},"作为安全载体，直接传递给加解密函数使用",[266,3501,3502,3505,3508,3510],{},[283,3503,3504],{},"CryptoKeyPair",[283,3506,3507],{},"密钥对容器",[283,3509,3496],{},[283,3511,3512],{},"封装返回包含publicKey和privateKey的密钥对对象",[17,3514,3516],{"id":3515},"_5-web-crypto-api支持的主流算法","5. Web Crypto API支持的主流算法",[65,3518,3519],{},[68,3520,3521],{},[71,3522,3523],{},"对称加密（Symmetric）表",[260,3525,3526,3542],{},[263,3527,3528],{},[266,3529,3530,3533,3536,3539],{},[269,3531,3532],{},"算法标识符",[269,3534,3535],{},"支持的操作",[269,3537,3538],{},"推荐搭配与技术参数",[269,3540,3541],{},"典型应用场景",[278,3543,3544,3558,3571],{},[266,3545,3546,3549,3552,3555],{},[283,3547,3548],{},"AES-GCM（强烈推荐）",[283,3550,3551],{},"encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey",[283,3553,3554],{},"密钥支持128\u002F192\u002F256位，IV推荐96位（12字节）",[283,3556,3557],{},"传输数据加解密、本地缓存加密，自带防篡改认证标签",[266,3559,3560,3563,3565,3568],{},[283,3561,3562],{},"AES-CBC",[283,3564,3551],{},[283,3566,3567],{},"密钥支持128\u002F192\u002F256位，IV固定128位（16字节）",[283,3569,3570],{},"兼容老旧遗留系统的对称加解密业务",[266,3572,3573,3576,3578,3581],{},[283,3574,3575],{},"AES-CTR",[283,3577,3551],{},[283,3579,3580],{},"计数器模式，支持流式处理",[283,3582,3583],{},"高频非对齐的流式数据区块加解密",[128,3585,3586,3589,3592],{},[68,3587,3588],{},"AES-GCM 是当前Web场景下的首选方案，硬件加速支持度高，同时自带消息认证能力，无需额外实现哈希校验逻辑，安全性和开发效率都更优。",[68,3590,3591],{},"不同算法的IV长度要求严格，比如AES-CBC必须使用固定16字节的IV，否则会直接触发解密失败。",[68,3593,3594],{},"三款算法都完整覆盖了密钥生成、导入导出、包裹\u002F解包裹密钥的全流程操作，完全适配Web Crypto的全场景开发需求。",[65,3596,3597],{"start":506},[68,3598,3599],{},[71,3600,3601],{},"非对称加密算法（Asymmetric）表",[260,3603,3604,3616],{},[263,3605,3606],{},[266,3607,3608,3610,3612,3614],{},[269,3609,3532],{},[269,3611,3535],{},[269,3613,3538],{},[269,3615,3541],{},[278,3617,3618],{},[266,3619,3620,3623,3626,3629],{},[283,3621,3622],{},"RSA-OAEP（强烈推荐）",[283,3624,3625],{},"encrypt, decrypt, generateKey, importKey, exportKey, wrapKey, unwrapKey",[283,3627,3628],{},"模数：2048, 4096 位哈希：推荐 SHA-256",[283,3630,3631],{},"混合加密体系中用于加密“对称密钥”，或加密超短敏感字段",[13,3633,3634],{},"RSA-OAEP是Web场景下非对称加密的首选方案，它通过最优非对称加密填充机制，解决了传统“教科书RSA”的密文可延展性安全缺陷，在随机预言模型下可以抵御适应性选择密文攻击。由于非对称加密的特性，它不适合直接加密大体积数据，行业通用的最佳实践就是用它加密AES等对称密钥，再用对称算法加密实际业务数据，兼顾安全性和性能效率。",[65,3636,3637],{"start":535},[68,3638,3639],{},[71,3640,3641],{},"数字签名算法（Signature）表",[260,3643,3644,3657],{},[263,3645,3646],{},[266,3647,3648,3651,3653,3655],{},[269,3649,3650],{},"算法名称",[269,3652,3535],{},[269,3654,3538],{},[269,3656,3541],{},[278,3658,3659,3673,3686,3699],{},[266,3660,3661,3664,3667,3670],{},[283,3662,3663],{},"ECDSA（强烈推荐）",[283,3665,3666],{},"sign、verify、generateKey、importKey、exportKey",[283,3668,3669],{},"椭圆曲线：P-256、P-384、P-521",[283,3671,3672],{},"现代高并发身份验签、JWT签名、区块链节点验签",[266,3674,3675,3678,3680,3683],{},[283,3676,3677],{},"RSASSA-PKCS1-v1_5",[283,3679,3666],{},[283,3681,3682],{},"模数≥2048位，哈希推荐SHA-256",[283,3684,3685],{},"传统企业级API签名、老旧版本JWT签名验签",[266,3687,3688,3691,3693,3696],{},[283,3689,3690],{},"RSA-PSS",[283,3692,3666],{},[283,3694,3695],{},"RSA概率签名方案，密码学严谨度极高",[283,3697,3698],{},"金融级或司法级等高安全要求的数字签名场景",[266,3700,3701,3704,3706,3709],{},[283,3702,3703],{},"HMAC",[283,3705,3666],{},[283,3707,3708],{},"基于哈希的密钥消息认证码",[283,3710,3711],{},"前后端API请求防篡改哈希签名，如AWS签名机制",[128,3713,3714,3717,3720],{},[68,3715,3716],{},"ECDSA 是Web场景下的首选签名方案，密钥体积小、验签速度快，非常适配高并发的现代Web服务。",[68,3718,3719],{},"RSA系列算法兼容性极强，适合需要对接老旧系统的业务场景，其中RSA-PSS的抗攻击能力远高于传统的RSASSA-PKCS1-v1_5。",[68,3721,3722],{},"HMAC实现成本低，是轻量API防篡改签名的最优选择。",[65,3724,3725],{"start":609},[68,3726,3727],{},[71,3728,3729],{},"单向哈希算法（Digest）表",[260,3731,3732,3744],{},[263,3733,3734],{},[266,3735,3736,3738,3740,3742],{},[269,3737,3650],{},[269,3739,3535],{},[269,3741,3538],{},[269,3743,3541],{},[278,3745,3746,3760,3773],{},[266,3747,3748,3751,3754,3757],{},[283,3749,3750],{},"SHA-256",[283,3752,3753],{},"digest",[283,3755,3756],{},"无需密钥，单向不可逆",[283,3758,3759],{},"文件完整性校验（秒传场景）、数据防篡改指纹生成",[266,3761,3762,3765,3767,3770],{},[283,3763,3764],{},"SHA-384",[283,3766,3753],{},[283,3768,3769],{},"无需密钥，单向不可逆，安全强度高于SHA-256",[283,3771,3772],{},"高敏感数据校验、金融级业务的完整性验证",[266,3774,3775,3778,3780,3783],{},[283,3776,3777],{},"SHA-512",[283,3779,3753],{},[283,3781,3782],{},"无需密钥，单向不可逆，安全强度最高",[283,3784,3785],{},"司法级、军工级等高安全要求场景的哈希校验",[13,3787,3788],{},"这三款同属SHA-2家族哈希算法，都具备单向不可逆的核心特性，无法从最终哈希值反推原始输入内容，仅用于生成数据唯一指纹，不能直接用于加解密还原原始数据。安全强度随输出位数提升而升高，同时计算性能也会小幅下降，普通业务场景优先选择SHA-256即可满足需求。",[65,3790,3791],{"start":624},[68,3792,3793],{},[71,3794,3795],{},"密钥派生算法（Key Derivation）表",[260,3797,3798,3810],{},[263,3799,3800],{},[266,3801,3802,3804,3806,3808],{},[269,3803,3650],{},[269,3805,3535],{},[269,3807,3538],{},[269,3809,3541],{},[278,3811,3812,3826,3840],{},[266,3813,3814,3817,3820,3823],{},[283,3815,3816],{},"ECDH",[283,3818,3819],{},"deriveKey、deriveBits、generateKey、importKey、exportKey",[283,3821,3822],{},"椭圆曲线：Diffie-Hellman",[283,3824,3825],{},"两个终端在不安全信道中，安全地协商出一致的对称密钥",[266,3827,3828,3831,3834,3837],{},[283,3829,3830],{},"PBKDF2",[283,3832,3833],{},"deriveKey、deriveBits、importKey",[283,3835,3836],{},"基于密码的密钥派生，推荐迭代≥100000次",[283,3838,3839],{},"将用户输入的简短文明密码，转换为符合AES标准的高强度密钥",[266,3841,3842,3845,3847,3850],{},[283,3843,3844],{},"HKDF",[283,3846,3833],{},[283,3848,3849],{},"基于HMAC的密钥扩展算法",[283,3851,3852],{},"从一个基础主密钥，安全地扩展派生出多个子密钥",[128,3854,3855,3858,3861],{},[68,3856,3857],{},"ECDH 是现代网络通信中密钥协商的核心方案，双方全程不需要直接传输最终的共享密钥，仅通过交换椭圆曲线公钥就能各自计算出完全一致的会话密钥，安全性极高。",[68,3859,3860],{},"PBKDF2 专门针对低熵的用户密码设计，通过高迭代次数大幅提升暴力破解的成本，是密码类密钥衍生的行业标准方案。",[68,3862,3863],{},"HKDF 面向已经具备高熵的主密钥材料，能高效地将单个主密钥拆分扩展为多个用途独立的子密钥，广泛应用于TLS等现代加密协议中。",[17,3865,3867],{"id":3866},"_6-web-crypto-api-不支持的主流算法","6. Web Crypto API 不支持的主流算法",[260,3869,3870,3882],{},[263,3871,3872],{},[266,3873,3874,3876,3879],{},[269,3875,3650],{},[269,3877,3878],{},"不支持原因",[269,3880,3881],{},"替代实现方案",[278,3883,3884,3895,3906],{},[266,3885,3886,3889,3892],{},[283,3887,3888],{},"MD5",[283,3890,3891],{},"已被密码学界证实存在碰撞漏洞，安全性完全失效",[283,3893,3894],{},"引入crypto-js等第三方库实现或使用 SHA-256",[266,3896,3897,3900,3903],{},[283,3898,3899],{},"SM2\u002FSM3\u002FSM4国密系列",[283,3901,3902],{},"浏览器国际标准未原生内置国密算法",[283,3904,3905],{},"引入sm-crypto等国密第三方库实现",[266,3907,3908,3911,3914],{},[283,3909,3910],{},"ChaCha20-Poly1305\u002FEd25519",[283,3912,3913],{},"目前尚未完全普及到所有主流浏览器的 crypto.subtle 中",[283,3915,3916],{},"引入第三方加密库补充实现",[17,3918,3920],{"id":3919},"_7-常用操作示例","7. 常用操作示例",[13,3922,3923],{},"以下是整理的 Web Crypto API (crypto.subtle) 最核心、最常用的四大标准代码示例（哈希生成、密钥生成、加密、解密）。需要特别提醒的是：所有 crypto.subtle 的操作都是异步的，都会返回一个 Promise，因此必须配合 async\u002Fawait 使用。",[123,3925,3927],{"id":3926},"_71-对称加密aes-gcm示例","7.1 对称加密AES-GCM示例",[13,3929,3930],{},"AES-GCM (Galois\u002FCounter Mode) 是一种非常流行且强大的认证加密（AEAD, Authenticated Encryption with Associated Data） 模式。你可以把它理解为一种“二合一”的加密模式：它在高效加密数据的同时，还能确保数据没有被篡改。",[13,3932,3933],{},"AES-GCM 是兼具高性能和高安全性的认证加密模式，特别适合在 Web Crypto API 这类原生环境中使用。它的安全性高度依赖于每次加密都使用唯一且不可预测的 IV。遵循这一核心原则，并妥善管理密钥、认真验证认证标签，就能为你的 Web 应用构建起一道坚实的数据安全防线。",[65,3935,3936],{},[68,3937,3938,3941],{},[71,3939,3940],{},"计算文本哈希摘要 (Digest)","：这是最简单的操作，常用于生成文件的 SHA-256。它不需要密钥。",[679,3943,3947],{"className":3944,"code":3945,"language":3946,"meta":502,"style":502},"language-js shiki shiki-themes material-theme-lighter material-theme material-theme-palenight","\u002F**\n * 计算字符串的 SHA-256 哈希值\n * @param {string} text - 输入的文本\n * @returns {Promise\u003Cstring>} Hex 格式的哈希字符串\n *\u002F\nasync function generateHash(text) {\n    \u002F\u002F 1. 将文本编码为 Uint8Array 字节流\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    \u002F\u002F 2. 使用 crypto.subtle.digest 生成哈希（返回 ArrayBuffer）\n    const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n    \u002F\u002F 3. 将 ArrayBuffer 转换为常见的十六进制 (Hex) 字符串\n    const hashArray = Array.from(new Uint8Array(hashBuffer));\n    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n    \n    return hashHex;\n}\n\n\u002F\u002F 使用示例：\nconst sha256Result = await generateHash(\"Hello World\");\n","js",[42,3948,3949,3954,3959,3987,4006,4011,4032,4037,4060,4084,4089,4094,4135,4139,4144,4179,4252,4257,4266,4271,4275,4280],{"__ignoreMap":502},[688,3950,3951],{"class":690,"line":691},[688,3952,3953],{"class":859},"\u002F**\n",[688,3955,3956],{"class":690,"line":506},[688,3957,3958],{"class":859}," * 计算字符串的 SHA-256 哈希值\n",[688,3960,3961,3964,3967,3971,3974,3978,3980,3984],{"class":690,"line":535},[688,3962,3963],{"class":859}," * ",[688,3965,3966],{"class":776},"@",[688,3968,3970],{"class":3969},"s6hCs","param",[688,3972,3973],{"class":776}," {",[688,3975,3977],{"class":3976},"sFweD","string",[688,3979,908],{"class":776},[688,3981,3983],{"class":3982},"sHdIc"," text",[688,3985,3986],{"class":859}," - 输入的文本\n",[688,3988,3989,3991,3993,3996,3998,4001,4003],{"class":690,"line":609},[688,3990,3963],{"class":859},[688,3992,3966],{"class":776},[688,3994,3995],{"class":3969},"returns",[688,3997,3973],{"class":776},[688,3999,4000],{"class":3976},"Promise\u003Cstring>",[688,4002,908],{"class":776},[688,4004,4005],{"class":859}," Hex 格式的哈希字符串\n",[688,4007,4008],{"class":690,"line":624},[688,4009,4010],{"class":859}," *\u002F\n",[688,4012,4013,4016,4019,4022,4024,4027,4030],{"class":690,"line":856},[688,4014,4015],{"class":2260},"async",[688,4017,4018],{"class":2260}," function",[688,4020,4021],{"class":783}," generateHash",[688,4023,788],{"class":791},[688,4025,4026],{"class":3982},"text",[688,4028,4029],{"class":791},")",[688,4031,871],{"class":791},[688,4033,4034],{"class":690,"line":863},[688,4035,4036],{"class":859},"    \u002F\u002F 1. 将文本编码为 Uint8Array 字节流\n",[688,4038,4039,4042,4045,4048,4051,4054,4057],{"class":690,"line":874},[688,4040,4041],{"class":2260},"    const",[688,4043,4044],{"class":787}," encoder",[688,4046,4047],{"class":791}," =",[688,4049,4050],{"class":791}," new",[688,4052,4053],{"class":783}," TextEncoder",[688,4055,4056],{"class":797},"()",[688,4058,4059],{"class":791},";\n",[688,4061,4062,4064,4067,4069,4071,4073,4076,4078,4080,4082],{"class":690,"line":890},[688,4063,4041],{"class":2260},[688,4065,4066],{"class":787}," data",[688,4068,4047],{"class":791},[688,4070,4044],{"class":787},[688,4072,2304],{"class":791},[688,4074,4075],{"class":783},"encode",[688,4077,788],{"class":797},[688,4079,4026],{"class":787},[688,4081,4029],{"class":797},[688,4083,4059],{"class":791},[688,4085,4086],{"class":690,"line":896},[688,4087,4088],{"emptyLinePlaceholder":1003},"\n",[688,4090,4091],{"class":690,"line":905},[688,4092,4093],{"class":859},"    \u002F\u002F 2. 使用 crypto.subtle.digest 生成哈希（返回 ArrayBuffer）\n",[688,4095,4096,4098,4101,4103,4106,4109,4111,4114,4116,4118,4120,4122,4124,4126,4129,4131,4133],{"class":690,"line":1334},[688,4097,4041],{"class":2260},[688,4099,4100],{"class":787}," hashBuffer",[688,4102,4047],{"class":791},[688,4104,4105],{"class":776}," await",[688,4107,4108],{"class":787}," crypto",[688,4110,2304],{"class":791},[688,4112,4113],{"class":787},"subtle",[688,4115,2304],{"class":791},[688,4117,3753],{"class":783},[688,4119,788],{"class":797},[688,4121,807],{"class":791},[688,4123,3750],{"class":697},[688,4125,807],{"class":791},[688,4127,4128],{"class":791},",",[688,4130,4066],{"class":787},[688,4132,4029],{"class":797},[688,4134,4059],{"class":791},[688,4136,4137],{"class":690,"line":1343},[688,4138,4088],{"emptyLinePlaceholder":1003},[688,4140,4141],{"class":690,"line":1351},[688,4142,4143],{"class":859},"    \u002F\u002F 3. 将 ArrayBuffer 转换为常见的十六进制 (Hex) 字符串\n",[688,4145,4146,4148,4151,4153,4156,4158,4161,4163,4166,4169,4171,4174,4177],{"class":690,"line":1359},[688,4147,4041],{"class":2260},[688,4149,4150],{"class":787}," hashArray",[688,4152,4047],{"class":791},[688,4154,4155],{"class":787}," Array",[688,4157,2304],{"class":791},[688,4159,4160],{"class":783},"from",[688,4162,788],{"class":797},[688,4164,4165],{"class":791},"new",[688,4167,4168],{"class":783}," Uint8Array",[688,4170,788],{"class":797},[688,4172,4173],{"class":787},"hashBuffer",[688,4175,4176],{"class":797},"))",[688,4178,4059],{"class":791},[688,4180,4181,4183,4186,4188,4190,4192,4195,4197,4200,4203,4206,4208,4211,4213,4216,4218,4220,4223,4225,4228,4230,4232,4234,4236,4238,4240,4243,4245,4248,4250],{"class":690,"line":1367},[688,4182,4041],{"class":2260},[688,4184,4185],{"class":787}," hashHex",[688,4187,4047],{"class":791},[688,4189,4150],{"class":787},[688,4191,2304],{"class":791},[688,4193,4194],{"class":783},"map",[688,4196,788],{"class":797},[688,4198,4199],{"class":3982},"b",[688,4201,4202],{"class":2260}," =>",[688,4204,4205],{"class":787}," b",[688,4207,2304],{"class":791},[688,4209,4210],{"class":783},"toString",[688,4212,788],{"class":797},[688,4214,4215],{"class":2478},"16",[688,4217,4029],{"class":797},[688,4219,2304],{"class":791},[688,4221,4222],{"class":783},"padStart",[688,4224,788],{"class":797},[688,4226,4227],{"class":2478},"2",[688,4229,4128],{"class":791},[688,4231,831],{"class":791},[688,4233,2499],{"class":697},[688,4235,807],{"class":791},[688,4237,4176],{"class":797},[688,4239,2304],{"class":791},[688,4241,4242],{"class":783},"join",[688,4244,788],{"class":797},[688,4246,4247],{"class":791},"''",[688,4249,4029],{"class":797},[688,4251,4059],{"class":791},[688,4253,4254],{"class":690,"line":1375},[688,4255,4256],{"class":797},"    \n",[688,4258,4259,4262,4264],{"class":690,"line":1383},[688,4260,4261],{"class":776},"    return",[688,4263,4185],{"class":787},[688,4265,4059],{"class":791},[688,4267,4268],{"class":690,"line":1392},[688,4269,4270],{"class":791},"}\n",[688,4272,4273],{"class":690,"line":1400},[688,4274,4088],{"emptyLinePlaceholder":1003},[688,4276,4277],{"class":690,"line":1408},[688,4278,4279],{"class":859},"\u002F\u002F 使用示例：\n",[688,4281,4282,4285,4288,4290,4292,4294,4296,4298,4301,4303,4305],{"class":690,"line":1416},[688,4283,4284],{"class":2260},"const",[688,4286,4287],{"class":787}," sha256Result ",[688,4289,2266],{"class":791},[688,4291,4105],{"class":776},[688,4293,4021],{"class":783},[688,4295,788],{"class":787},[688,4297,2269],{"class":791},[688,4299,4300],{"class":697},"Hello World",[688,4302,2269],{"class":791},[688,4304,4029],{"class":787},[688,4306,4059],{"class":791},[65,4308,4309],{"start":506},[68,4310,4311,4314],{},[71,4312,4313],{},"生成对称加密密钥 (Generate Key)","：在加密数据之前，必须先通过 subtle 算法生成一个标准的加密密钥对象（CryptoKey）。这里以主流的 AES-GCM 算法为例：",[679,4316,4318],{"className":3944,"code":4317,"language":3946,"meta":502,"style":502},"\u002F**\n * 生成一个用于 AES-GCM 加密的 256 位随机密钥\n * @returns {Promise\u003CCryptoKey>} 返回 CryptoKey 对象\n *\u002F\nasync function generateAESKey() {\n    return await crypto.subtle.generateKey(\n        {\n        name: 'AES-GCM',\n        length: 256 \u002F\u002F 密钥长度支持 128, 192, 或 256 位\n        },\n        false, \u002F\u002F 是否允许该密钥被导出（如果设为 false，密钥无法脱离当前浏览器，安全性极高）\n        ['encrypt', 'decrypt'] \u002F\u002F 该密钥被允许用来做什么操作\n    );\n}\n\n",[42,4319,4320,4324,4329,4347,4351,4364,4384,4389,4405,4418,4423,4434,4461,4468],{"__ignoreMap":502},[688,4321,4322],{"class":690,"line":691},[688,4323,3953],{"class":859},[688,4325,4326],{"class":690,"line":506},[688,4327,4328],{"class":859}," * 生成一个用于 AES-GCM 加密的 256 位随机密钥\n",[688,4330,4331,4333,4335,4337,4339,4342,4344],{"class":690,"line":535},[688,4332,3963],{"class":859},[688,4334,3966],{"class":776},[688,4336,3995],{"class":3969},[688,4338,3973],{"class":776},[688,4340,4341],{"class":3976},"Promise\u003CCryptoKey>",[688,4343,908],{"class":776},[688,4345,4346],{"class":859}," 返回 CryptoKey 对象\n",[688,4348,4349],{"class":690,"line":609},[688,4350,4010],{"class":859},[688,4352,4353,4355,4357,4360,4362],{"class":690,"line":624},[688,4354,4015],{"class":2260},[688,4356,4018],{"class":2260},[688,4358,4359],{"class":783}," generateAESKey",[688,4361,4056],{"class":791},[688,4363,871],{"class":791},[688,4365,4366,4368,4370,4372,4374,4376,4378,4381],{"class":690,"line":856},[688,4367,4261],{"class":776},[688,4369,4105],{"class":776},[688,4371,4108],{"class":787},[688,4373,2304],{"class":791},[688,4375,4113],{"class":787},[688,4377,2304],{"class":791},[688,4379,4380],{"class":783},"generateKey",[688,4382,4383],{"class":797},"(\n",[688,4385,4386],{"class":690,"line":863},[688,4387,4388],{"class":791},"        {\n",[688,4390,4391,4394,4396,4398,4401,4403],{"class":690,"line":874},[688,4392,4393],{"class":797},"        name",[688,4395,801],{"class":791},[688,4397,831],{"class":791},[688,4399,4400],{"class":697},"AES-GCM",[688,4402,807],{"class":791},[688,4404,838],{"class":791},[688,4406,4407,4410,4412,4415],{"class":690,"line":890},[688,4408,4409],{"class":797},"        length",[688,4411,801],{"class":791},[688,4413,4414],{"class":2478}," 256",[688,4416,4417],{"class":859}," \u002F\u002F 密钥长度支持 128, 192, 或 256 位\n",[688,4419,4420],{"class":690,"line":896},[688,4421,4422],{"class":791},"        },\n",[688,4424,4425,4429,4431],{"class":690,"line":905},[688,4426,4428],{"class":4427},"sfNiH","        false",[688,4430,4128],{"class":791},[688,4432,4433],{"class":859}," \u002F\u002F 是否允许该密钥被导出（如果设为 false，密钥无法脱离当前浏览器，安全性极高）\n",[688,4435,4436,4439,4441,4444,4446,4448,4450,4453,4455,4458],{"class":690,"line":1334},[688,4437,4438],{"class":797},"        [",[688,4440,807],{"class":791},[688,4442,4443],{"class":697},"encrypt",[688,4445,807],{"class":791},[688,4447,4128],{"class":791},[688,4449,831],{"class":791},[688,4451,4452],{"class":697},"decrypt",[688,4454,807],{"class":791},[688,4456,4457],{"class":797},"] ",[688,4459,4460],{"class":859},"\u002F\u002F 该密钥被允许用来做什么操作\n",[688,4462,4463,4466],{"class":690,"line":1343},[688,4464,4465],{"class":797},"    )",[688,4467,4059],{"class":791},[688,4469,4470],{"class":690,"line":1351},[688,4471,4270],{"class":791},[65,4473,4474],{"start":535},[68,4475,4476,4479],{},[71,4477,4478],{},"数据加密 Encrypt","：使用生成的密钥和 AES-GCM 算法对敏感文本进行加密。为了安全，AES-GCM 要求每次加密必须配合一个随机的初始化向量 (IV)。",[679,4481,4483],{"className":3944,"code":4482,"language":3946,"meta":502,"style":502},"\u002F**\n * 对文本进行 AES-GCM 加密\n * @param {string} text - 待加密的明文\n * @param {CryptoKey} key - 上一步生成的 CryptoKey 对象\n * @returns {Promise\u003C{cipherText: ArrayBuffer, iv: Uint8Array}>} 返回密文和IV\n *\u002F\nasync function encryptText(text, key) {\n    const encoder = new TextEncoder();\n    const encodedText = encoder.encode(text);\n\n    \u002F\u002F AES-GCM 推荐使用 12位（96位）的高安全伪随机数作为 IV\n    \u002F\u002F 注意：这里使用的是外部同步方法 getRandomValues 生成随机数\n    const iv = crypto.getRandomValues(new Uint8Array(12));\n\n    \u002F\u002F 执行加密\n    const cipherText = await crypto.subtle.encrypt(\n        {\n        name: 'AES-GCM',\n        iv: iv \u002F\u002F 加密参数必须包含 IV\n        },\n        key, \u002F\u002F 密钥\n        encodedText \u002F\u002F 明文数据\n    );\n\n    \u002F\u002F 需要同时把密文和 IV 返回，因为解密时必须提供一模一样的 IV\n    return { cipherText, iv };\n}\n\n",[42,4484,4485,4489,4494,4513,4533,4551,4555,4576,4592,4615,4619,4624,4629,4660,4664,4669,4692,4696,4710,4722,4726,4736,4744,4750,4754,4759,4774],{"__ignoreMap":502},[688,4486,4487],{"class":690,"line":691},[688,4488,3953],{"class":859},[688,4490,4491],{"class":690,"line":506},[688,4492,4493],{"class":859}," * 对文本进行 AES-GCM 加密\n",[688,4495,4496,4498,4500,4502,4504,4506,4508,4510],{"class":690,"line":535},[688,4497,3963],{"class":859},[688,4499,3966],{"class":776},[688,4501,3970],{"class":3969},[688,4503,3973],{"class":776},[688,4505,3977],{"class":3976},[688,4507,908],{"class":776},[688,4509,3983],{"class":3982},[688,4511,4512],{"class":859}," - 待加密的明文\n",[688,4514,4515,4517,4519,4521,4523,4525,4527,4530],{"class":690,"line":609},[688,4516,3963],{"class":859},[688,4518,3966],{"class":776},[688,4520,3970],{"class":3969},[688,4522,3973],{"class":776},[688,4524,3490],{"class":3976},[688,4526,908],{"class":776},[688,4528,4529],{"class":3982}," key",[688,4531,4532],{"class":859}," - 上一步生成的 CryptoKey 对象\n",[688,4534,4535,4537,4539,4541,4543,4546,4548],{"class":690,"line":624},[688,4536,3963],{"class":859},[688,4538,3966],{"class":776},[688,4540,3995],{"class":3969},[688,4542,3973],{"class":776},[688,4544,4545],{"class":3976},"Promise\u003C{cipherText: ArrayBuffer, iv: Uint8Array}>",[688,4547,908],{"class":776},[688,4549,4550],{"class":859}," 返回密文和IV\n",[688,4552,4553],{"class":690,"line":856},[688,4554,4010],{"class":859},[688,4556,4557,4559,4561,4564,4566,4568,4570,4572,4574],{"class":690,"line":863},[688,4558,4015],{"class":2260},[688,4560,4018],{"class":2260},[688,4562,4563],{"class":783}," encryptText",[688,4565,788],{"class":791},[688,4567,4026],{"class":3982},[688,4569,4128],{"class":791},[688,4571,4529],{"class":3982},[688,4573,4029],{"class":791},[688,4575,871],{"class":791},[688,4577,4578,4580,4582,4584,4586,4588,4590],{"class":690,"line":874},[688,4579,4041],{"class":2260},[688,4581,4044],{"class":787},[688,4583,4047],{"class":791},[688,4585,4050],{"class":791},[688,4587,4053],{"class":783},[688,4589,4056],{"class":797},[688,4591,4059],{"class":791},[688,4593,4594,4596,4599,4601,4603,4605,4607,4609,4611,4613],{"class":690,"line":890},[688,4595,4041],{"class":2260},[688,4597,4598],{"class":787}," encodedText",[688,4600,4047],{"class":791},[688,4602,4044],{"class":787},[688,4604,2304],{"class":791},[688,4606,4075],{"class":783},[688,4608,788],{"class":797},[688,4610,4026],{"class":787},[688,4612,4029],{"class":797},[688,4614,4059],{"class":791},[688,4616,4617],{"class":690,"line":896},[688,4618,4088],{"emptyLinePlaceholder":1003},[688,4620,4621],{"class":690,"line":905},[688,4622,4623],{"class":859},"    \u002F\u002F AES-GCM 推荐使用 12位（96位）的高安全伪随机数作为 IV\n",[688,4625,4626],{"class":690,"line":1334},[688,4627,4628],{"class":859},"    \u002F\u002F 注意：这里使用的是外部同步方法 getRandomValues 生成随机数\n",[688,4630,4631,4633,4636,4638,4640,4642,4645,4647,4649,4651,4653,4656,4658],{"class":690,"line":1343},[688,4632,4041],{"class":2260},[688,4634,4635],{"class":787}," iv",[688,4637,4047],{"class":791},[688,4639,4108],{"class":787},[688,4641,2304],{"class":791},[688,4643,4644],{"class":783},"getRandomValues",[688,4646,788],{"class":797},[688,4648,4165],{"class":791},[688,4650,4168],{"class":783},[688,4652,788],{"class":797},[688,4654,4655],{"class":2478},"12",[688,4657,4176],{"class":797},[688,4659,4059],{"class":791},[688,4661,4662],{"class":690,"line":1351},[688,4663,4088],{"emptyLinePlaceholder":1003},[688,4665,4666],{"class":690,"line":1359},[688,4667,4668],{"class":859},"    \u002F\u002F 执行加密\n",[688,4670,4671,4673,4676,4678,4680,4682,4684,4686,4688,4690],{"class":690,"line":1367},[688,4672,4041],{"class":2260},[688,4674,4675],{"class":787}," cipherText",[688,4677,4047],{"class":791},[688,4679,4105],{"class":776},[688,4681,4108],{"class":787},[688,4683,2304],{"class":791},[688,4685,4113],{"class":787},[688,4687,2304],{"class":791},[688,4689,4443],{"class":783},[688,4691,4383],{"class":797},[688,4693,4694],{"class":690,"line":1375},[688,4695,4388],{"class":791},[688,4697,4698,4700,4702,4704,4706,4708],{"class":690,"line":1383},[688,4699,4393],{"class":797},[688,4701,801],{"class":791},[688,4703,831],{"class":791},[688,4705,4400],{"class":697},[688,4707,807],{"class":791},[688,4709,838],{"class":791},[688,4711,4712,4715,4717,4719],{"class":690,"line":1392},[688,4713,4714],{"class":797},"        iv",[688,4716,801],{"class":791},[688,4718,4635],{"class":787},[688,4720,4721],{"class":859}," \u002F\u002F 加密参数必须包含 IV\n",[688,4723,4724],{"class":690,"line":1400},[688,4725,4422],{"class":791},[688,4727,4728,4731,4733],{"class":690,"line":1408},[688,4729,4730],{"class":787},"        key",[688,4732,4128],{"class":791},[688,4734,4735],{"class":859}," \u002F\u002F 密钥\n",[688,4737,4738,4741],{"class":690,"line":1416},[688,4739,4740],{"class":787},"        encodedText",[688,4742,4743],{"class":859}," \u002F\u002F 明文数据\n",[688,4745,4746,4748],{"class":690,"line":1425},[688,4747,4465],{"class":797},[688,4749,4059],{"class":791},[688,4751,4752],{"class":690,"line":1434},[688,4753,4088],{"emptyLinePlaceholder":1003},[688,4755,4756],{"class":690,"line":1443},[688,4757,4758],{"class":859},"    \u002F\u002F 需要同时把密文和 IV 返回，因为解密时必须提供一模一样的 IV\n",[688,4760,4761,4763,4765,4767,4769,4771],{"class":690,"line":1452},[688,4762,4261],{"class":776},[688,4764,3973],{"class":791},[688,4766,4675],{"class":787},[688,4768,4128],{"class":791},[688,4770,4635],{"class":787},[688,4772,4773],{"class":791}," };\n",[688,4775,4776],{"class":690,"line":1461},[688,4777,4270],{"class":791},[65,4779,4780],{"start":609},[68,4781,4782,4785],{},[71,4783,4784],{},"数据解密 Decrypt","：传入加密时使用的 CryptoKey、IV 和密文数组，将其还原为人类可读的明文字符串。",[679,4787,4789],{"className":3944,"code":4788,"language":3946,"meta":502,"style":502},"\u002F**\n * 对 AES-GCM 密文进行解密\n * @param {ArrayBuffer} cipherText - 密文数据\n * @param {CryptoKey} key - 加密时使用的同一个密钥\n * @param {Uint8Array} iv - 加密时对应的随机 IV\n * @returns {Promise\u003Cstring>} 解密后的明文字符串\n *\u002F\nasync function decryptText(cipherText, key, iv) {\n    \u002F\u002F 执行解密\n    const decryptedBuffer = await crypto.subtle.decrypt(\n        {\n        name: 'AES-GCM',\n        iv: iv \u002F\u002F 必须传入与加密时完全相同的 IV\n        },\n        key,\n        cipherText\n    );\n\n    \u002F\u002F 将 ArrayBuffer 字节解码还原为字符串\n    const decoder = new TextDecoder();\n    return decoder.decode(decryptedBuffer);\n}\n",[42,4790,4791,4795,4800,4820,4839,4859,4876,4880,4906,4911,4934,4938,4952,4963,4967,4973,4978,4984,4988,4993,5011,5031],{"__ignoreMap":502},[688,4792,4793],{"class":690,"line":691},[688,4794,3953],{"class":859},[688,4796,4797],{"class":690,"line":506},[688,4798,4799],{"class":859}," * 对 AES-GCM 密文进行解密\n",[688,4801,4802,4804,4806,4808,4810,4813,4815,4817],{"class":690,"line":535},[688,4803,3963],{"class":859},[688,4805,3966],{"class":776},[688,4807,3970],{"class":3969},[688,4809,3973],{"class":776},[688,4811,4812],{"class":3976},"ArrayBuffer",[688,4814,908],{"class":776},[688,4816,4675],{"class":3982},[688,4818,4819],{"class":859}," - 密文数据\n",[688,4821,4822,4824,4826,4828,4830,4832,4834,4836],{"class":690,"line":609},[688,4823,3963],{"class":859},[688,4825,3966],{"class":776},[688,4827,3970],{"class":3969},[688,4829,3973],{"class":776},[688,4831,3490],{"class":3976},[688,4833,908],{"class":776},[688,4835,4529],{"class":3982},[688,4837,4838],{"class":859}," - 加密时使用的同一个密钥\n",[688,4840,4841,4843,4845,4847,4849,4852,4854,4856],{"class":690,"line":624},[688,4842,3963],{"class":859},[688,4844,3966],{"class":776},[688,4846,3970],{"class":3969},[688,4848,3973],{"class":776},[688,4850,4851],{"class":3976},"Uint8Array",[688,4853,908],{"class":776},[688,4855,4635],{"class":3982},[688,4857,4858],{"class":859}," - 加密时对应的随机 IV\n",[688,4860,4861,4863,4865,4867,4869,4871,4873],{"class":690,"line":856},[688,4862,3963],{"class":859},[688,4864,3966],{"class":776},[688,4866,3995],{"class":3969},[688,4868,3973],{"class":776},[688,4870,4000],{"class":3976},[688,4872,908],{"class":776},[688,4874,4875],{"class":859}," 解密后的明文字符串\n",[688,4877,4878],{"class":690,"line":863},[688,4879,4010],{"class":859},[688,4881,4882,4884,4886,4889,4891,4894,4896,4898,4900,4902,4904],{"class":690,"line":874},[688,4883,4015],{"class":2260},[688,4885,4018],{"class":2260},[688,4887,4888],{"class":783}," decryptText",[688,4890,788],{"class":791},[688,4892,4893],{"class":3982},"cipherText",[688,4895,4128],{"class":791},[688,4897,4529],{"class":3982},[688,4899,4128],{"class":791},[688,4901,4635],{"class":3982},[688,4903,4029],{"class":791},[688,4905,871],{"class":791},[688,4907,4908],{"class":690,"line":890},[688,4909,4910],{"class":859},"    \u002F\u002F 执行解密\n",[688,4912,4913,4915,4918,4920,4922,4924,4926,4928,4930,4932],{"class":690,"line":896},[688,4914,4041],{"class":2260},[688,4916,4917],{"class":787}," decryptedBuffer",[688,4919,4047],{"class":791},[688,4921,4105],{"class":776},[688,4923,4108],{"class":787},[688,4925,2304],{"class":791},[688,4927,4113],{"class":787},[688,4929,2304],{"class":791},[688,4931,4452],{"class":783},[688,4933,4383],{"class":797},[688,4935,4936],{"class":690,"line":905},[688,4937,4388],{"class":791},[688,4939,4940,4942,4944,4946,4948,4950],{"class":690,"line":1334},[688,4941,4393],{"class":797},[688,4943,801],{"class":791},[688,4945,831],{"class":791},[688,4947,4400],{"class":697},[688,4949,807],{"class":791},[688,4951,838],{"class":791},[688,4953,4954,4956,4958,4960],{"class":690,"line":1343},[688,4955,4714],{"class":797},[688,4957,801],{"class":791},[688,4959,4635],{"class":787},[688,4961,4962],{"class":859}," \u002F\u002F 必须传入与加密时完全相同的 IV\n",[688,4964,4965],{"class":690,"line":1351},[688,4966,4422],{"class":791},[688,4968,4969,4971],{"class":690,"line":1359},[688,4970,4730],{"class":787},[688,4972,838],{"class":791},[688,4974,4975],{"class":690,"line":1367},[688,4976,4977],{"class":787},"        cipherText\n",[688,4979,4980,4982],{"class":690,"line":1375},[688,4981,4465],{"class":797},[688,4983,4059],{"class":791},[688,4985,4986],{"class":690,"line":1383},[688,4987,4088],{"emptyLinePlaceholder":1003},[688,4989,4990],{"class":690,"line":1392},[688,4991,4992],{"class":859},"    \u002F\u002F 将 ArrayBuffer 字节解码还原为字符串\n",[688,4994,4995,4997,5000,5002,5004,5007,5009],{"class":690,"line":1400},[688,4996,4041],{"class":2260},[688,4998,4999],{"class":787}," decoder",[688,5001,4047],{"class":791},[688,5003,4050],{"class":791},[688,5005,5006],{"class":783}," TextDecoder",[688,5008,4056],{"class":797},[688,5010,4059],{"class":791},[688,5012,5013,5015,5017,5019,5022,5024,5027,5029],{"class":690,"line":1408},[688,5014,4261],{"class":776},[688,5016,4999],{"class":787},[688,5018,2304],{"class":791},[688,5020,5021],{"class":783},"decode",[688,5023,788],{"class":797},[688,5025,5026],{"class":787},"decryptedBuffer",[688,5028,4029],{"class":797},[688,5030,4059],{"class":791},[688,5032,5033],{"class":690,"line":1416},[688,5034,4270],{"class":791},[2103,5036,5037],{},[13,5038,5039],{},"如果要在项目中保存或传输生成的密钥和密文，通常需要通过 crypto.subtle.exportKey('raw', key) 将密钥导出成字节，再用 btoa() 或 Base64 方案 转换成可传输的字符串格式。",[123,5041,5043],{"id":5042},"_72-非对称加密rsa-oaep示例","7.2 非对称加密RSA-OAEP示例",[13,5045,5046],{},"非对称加密使用公钥加密、私钥解密，常用于非对称场景或加密对称密钥（如密钥交换）。这里使用最安全的 RSA-OAEP 算法。",[65,5048,5049],{},[68,5050,5051],{},[71,5052,5053],{},"生成 RSA 密钥对",[679,5055,5057],{"className":3944,"code":5056,"language":3946,"meta":502,"style":502},"\u002F**\n * 生成用于 RSA-OAEP 加解密的 2048 位密钥对\n * @returns {Promise\u003CCryptoKeyPair>} 返回包含公钥和私钥的对象\n *\u002F\nasync function generateRSAKeyPair() {\n    return await crypto.subtle.generateKey(\n        {\n            name: \"RSA-OAEP\",\n            modulusLength: 2048, \u002F\u002F 密钥长度，2048 位是目前的标准安全线\n            publicExponent: new Uint8Array([1, 0, 1]), \u002F\u002F 等于 65537，固定的密码学标准常数\n            hash: \"SHA-256\", \u002F\u002F 内部使用的哈希算法\n        },\n        true, \u002F\u002F 是否允许导出\n        [\"encrypt\", \"decrypt\"] \u002F\u002F 用途分配\n    );\n}\n",[42,5058,5059,5063,5068,5086,5090,5103,5121,5125,5141,5156,5190,5208,5212,5222,5245,5251],{"__ignoreMap":502},[688,5060,5061],{"class":690,"line":691},[688,5062,3953],{"class":859},[688,5064,5065],{"class":690,"line":506},[688,5066,5067],{"class":859}," * 生成用于 RSA-OAEP 加解密的 2048 位密钥对\n",[688,5069,5070,5072,5074,5076,5078,5081,5083],{"class":690,"line":535},[688,5071,3963],{"class":859},[688,5073,3966],{"class":776},[688,5075,3995],{"class":3969},[688,5077,3973],{"class":776},[688,5079,5080],{"class":3976},"Promise\u003CCryptoKeyPair>",[688,5082,908],{"class":776},[688,5084,5085],{"class":859}," 返回包含公钥和私钥的对象\n",[688,5087,5088],{"class":690,"line":609},[688,5089,4010],{"class":859},[688,5091,5092,5094,5096,5099,5101],{"class":690,"line":624},[688,5093,4015],{"class":2260},[688,5095,4018],{"class":2260},[688,5097,5098],{"class":783}," generateRSAKeyPair",[688,5100,4056],{"class":791},[688,5102,871],{"class":791},[688,5104,5105,5107,5109,5111,5113,5115,5117,5119],{"class":690,"line":856},[688,5106,4261],{"class":776},[688,5108,4105],{"class":776},[688,5110,4108],{"class":787},[688,5112,2304],{"class":791},[688,5114,4113],{"class":787},[688,5116,2304],{"class":791},[688,5118,4380],{"class":783},[688,5120,4383],{"class":797},[688,5122,5123],{"class":690,"line":863},[688,5124,4388],{"class":791},[688,5126,5127,5130,5132,5134,5137,5139],{"class":690,"line":874},[688,5128,5129],{"class":797},"            name",[688,5131,801],{"class":791},[688,5133,2307],{"class":791},[688,5135,5136],{"class":697},"RSA-OAEP",[688,5138,2269],{"class":791},[688,5140,838],{"class":791},[688,5142,5143,5146,5148,5151,5153],{"class":690,"line":890},[688,5144,5145],{"class":797},"            modulusLength",[688,5147,801],{"class":791},[688,5149,5150],{"class":2478}," 2048",[688,5152,4128],{"class":791},[688,5154,5155],{"class":859}," \u002F\u002F 密钥长度，2048 位是目前的标准安全线\n",[688,5157,5158,5161,5163,5165,5167,5170,5172,5174,5177,5179,5182,5185,5187],{"class":690,"line":896},[688,5159,5160],{"class":797},"            publicExponent",[688,5162,801],{"class":791},[688,5164,4050],{"class":791},[688,5166,4168],{"class":783},[688,5168,5169],{"class":797},"([",[688,5171,2739],{"class":2478},[688,5173,4128],{"class":791},[688,5175,5176],{"class":2478}," 0",[688,5178,4128],{"class":791},[688,5180,5181],{"class":2478}," 1",[688,5183,5184],{"class":797},"])",[688,5186,4128],{"class":791},[688,5188,5189],{"class":859}," \u002F\u002F 等于 65537，固定的密码学标准常数\n",[688,5191,5192,5195,5197,5199,5201,5203,5205],{"class":690,"line":905},[688,5193,5194],{"class":797},"            hash",[688,5196,801],{"class":791},[688,5198,2307],{"class":791},[688,5200,3750],{"class":697},[688,5202,2269],{"class":791},[688,5204,4128],{"class":791},[688,5206,5207],{"class":859}," \u002F\u002F 内部使用的哈希算法\n",[688,5209,5210],{"class":690,"line":1334},[688,5211,4422],{"class":791},[688,5213,5214,5217,5219],{"class":690,"line":1343},[688,5215,5216],{"class":4427},"        true",[688,5218,4128],{"class":791},[688,5220,5221],{"class":859}," \u002F\u002F 是否允许导出\n",[688,5223,5224,5226,5228,5230,5232,5234,5236,5238,5240,5242],{"class":690,"line":1351},[688,5225,4438],{"class":797},[688,5227,2269],{"class":791},[688,5229,4443],{"class":697},[688,5231,2269],{"class":791},[688,5233,4128],{"class":791},[688,5235,2307],{"class":791},[688,5237,4452],{"class":697},[688,5239,2269],{"class":791},[688,5241,4457],{"class":797},[688,5243,5244],{"class":859},"\u002F\u002F 用途分配\n",[688,5246,5247,5249],{"class":690,"line":1359},[688,5248,4465],{"class":797},[688,5250,4059],{"class":791},[688,5252,5253],{"class":690,"line":1367},[688,5254,4270],{"class":791},[65,5256,5257],{"start":506},[68,5258,5259],{},[71,5260,5261],{},"使用公钥加密",[679,5263,5265],{"className":3944,"code":5264,"language":3946,"meta":502,"style":502},"\u002F**\n * 使用 RSA 公钥加密文本\n *\u002F\nasync function rsaEncrypt(text, publicKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    \u002F\u002F 返回 ArrayBuffer 格式的密文\n    return await crypto.subtle.encrypt(\n        { name: \"RSA-OAEP\" },\n        publicKey,\n        data\n    );\n}\n",[42,5266,5267,5271,5276,5280,5302,5318,5340,5344,5349,5367,5386,5393,5398,5404],{"__ignoreMap":502},[688,5268,5269],{"class":690,"line":691},[688,5270,3953],{"class":859},[688,5272,5273],{"class":690,"line":506},[688,5274,5275],{"class":859}," * 使用 RSA 公钥加密文本\n",[688,5277,5278],{"class":690,"line":535},[688,5279,4010],{"class":859},[688,5281,5282,5284,5286,5289,5291,5293,5295,5298,5300],{"class":690,"line":609},[688,5283,4015],{"class":2260},[688,5285,4018],{"class":2260},[688,5287,5288],{"class":783}," rsaEncrypt",[688,5290,788],{"class":791},[688,5292,4026],{"class":3982},[688,5294,4128],{"class":791},[688,5296,5297],{"class":3982}," publicKey",[688,5299,4029],{"class":791},[688,5301,871],{"class":791},[688,5303,5304,5306,5308,5310,5312,5314,5316],{"class":690,"line":624},[688,5305,4041],{"class":2260},[688,5307,4044],{"class":787},[688,5309,4047],{"class":791},[688,5311,4050],{"class":791},[688,5313,4053],{"class":783},[688,5315,4056],{"class":797},[688,5317,4059],{"class":791},[688,5319,5320,5322,5324,5326,5328,5330,5332,5334,5336,5338],{"class":690,"line":856},[688,5321,4041],{"class":2260},[688,5323,4066],{"class":787},[688,5325,4047],{"class":791},[688,5327,4044],{"class":787},[688,5329,2304],{"class":791},[688,5331,4075],{"class":783},[688,5333,788],{"class":797},[688,5335,4026],{"class":787},[688,5337,4029],{"class":797},[688,5339,4059],{"class":791},[688,5341,5342],{"class":690,"line":863},[688,5343,4088],{"emptyLinePlaceholder":1003},[688,5345,5346],{"class":690,"line":874},[688,5347,5348],{"class":859},"    \u002F\u002F 返回 ArrayBuffer 格式的密文\n",[688,5350,5351,5353,5355,5357,5359,5361,5363,5365],{"class":690,"line":890},[688,5352,4261],{"class":776},[688,5354,4105],{"class":776},[688,5356,4108],{"class":787},[688,5358,2304],{"class":791},[688,5360,4113],{"class":787},[688,5362,2304],{"class":791},[688,5364,4443],{"class":783},[688,5366,4383],{"class":797},[688,5368,5369,5372,5375,5377,5379,5381,5383],{"class":690,"line":896},[688,5370,5371],{"class":791},"        {",[688,5373,5374],{"class":797}," name",[688,5376,801],{"class":791},[688,5378,2307],{"class":791},[688,5380,5136],{"class":697},[688,5382,2269],{"class":791},[688,5384,5385],{"class":791}," },\n",[688,5387,5388,5391],{"class":690,"line":905},[688,5389,5390],{"class":787},"        publicKey",[688,5392,838],{"class":791},[688,5394,5395],{"class":690,"line":1334},[688,5396,5397],{"class":787},"        data\n",[688,5399,5400,5402],{"class":690,"line":1343},[688,5401,4465],{"class":797},[688,5403,4059],{"class":791},[688,5405,5406],{"class":690,"line":1351},[688,5407,4270],{"class":791},[65,5409,5410],{"start":535},[68,5411,5412],{},[71,5413,5414],{},"使用私钥解密",[679,5416,5418],{"className":3944,"code":5417,"language":3946,"meta":502,"style":502},"\u002F**\n * 使用 RSA 私钥解密文本\n *\u002F\nasync function rsaDecrypt(cipherText, privateKey) {\n    const decryptedBuffer = await crypto.subtle.decrypt(\n        { name: \"RSA-OAEP\" },\n        privateKey,\n        cipherText\n    );\n\n    const decoder = new TextDecoder();\n    return decoder.decode(decryptedBuffer);\n}\n",[42,5419,5420,5424,5429,5433,5455,5477,5493,5500,5504,5510,5514,5530,5548],{"__ignoreMap":502},[688,5421,5422],{"class":690,"line":691},[688,5423,3953],{"class":859},[688,5425,5426],{"class":690,"line":506},[688,5427,5428],{"class":859}," * 使用 RSA 私钥解密文本\n",[688,5430,5431],{"class":690,"line":535},[688,5432,4010],{"class":859},[688,5434,5435,5437,5439,5442,5444,5446,5448,5451,5453],{"class":690,"line":609},[688,5436,4015],{"class":2260},[688,5438,4018],{"class":2260},[688,5440,5441],{"class":783}," rsaDecrypt",[688,5443,788],{"class":791},[688,5445,4893],{"class":3982},[688,5447,4128],{"class":791},[688,5449,5450],{"class":3982}," privateKey",[688,5452,4029],{"class":791},[688,5454,871],{"class":791},[688,5456,5457,5459,5461,5463,5465,5467,5469,5471,5473,5475],{"class":690,"line":624},[688,5458,4041],{"class":2260},[688,5460,4917],{"class":787},[688,5462,4047],{"class":791},[688,5464,4105],{"class":776},[688,5466,4108],{"class":787},[688,5468,2304],{"class":791},[688,5470,4113],{"class":787},[688,5472,2304],{"class":791},[688,5474,4452],{"class":783},[688,5476,4383],{"class":797},[688,5478,5479,5481,5483,5485,5487,5489,5491],{"class":690,"line":856},[688,5480,5371],{"class":791},[688,5482,5374],{"class":797},[688,5484,801],{"class":791},[688,5486,2307],{"class":791},[688,5488,5136],{"class":697},[688,5490,2269],{"class":791},[688,5492,5385],{"class":791},[688,5494,5495,5498],{"class":690,"line":863},[688,5496,5497],{"class":787},"        privateKey",[688,5499,838],{"class":791},[688,5501,5502],{"class":690,"line":874},[688,5503,4977],{"class":787},[688,5505,5506,5508],{"class":690,"line":890},[688,5507,4465],{"class":797},[688,5509,4059],{"class":791},[688,5511,5512],{"class":690,"line":896},[688,5513,4088],{"emptyLinePlaceholder":1003},[688,5515,5516,5518,5520,5522,5524,5526,5528],{"class":690,"line":905},[688,5517,4041],{"class":2260},[688,5519,4999],{"class":787},[688,5521,4047],{"class":791},[688,5523,4050],{"class":791},[688,5525,5006],{"class":783},[688,5527,4056],{"class":797},[688,5529,4059],{"class":791},[688,5531,5532,5534,5536,5538,5540,5542,5544,5546],{"class":690,"line":1334},[688,5533,4261],{"class":776},[688,5535,4999],{"class":787},[688,5537,2304],{"class":791},[688,5539,5021],{"class":783},[688,5541,788],{"class":797},[688,5543,5026],{"class":787},[688,5545,4029],{"class":797},[688,5547,4059],{"class":791},[688,5549,5550],{"class":690,"line":1343},[688,5551,4270],{"class":791},[123,5553,5555],{"id":5554},"_73-数字签名与验证-rsassa-pkcs1-v1_5","7.3  数字签名与验证 (RSASSA-PKCS1-v1_5)",[13,5557,5558],{},"数字签名常用于防止数据被篡改并确认发送者身份。其逻辑是：私钥签名、公钥验证。这里使用最通用的 RSASSA-PKCS1-v1_5 算法。",[65,5560,5561],{},[68,5562,5563],{},[71,5564,5565],{},"生成数字签名的 RSA 密钥对",[679,5567,5569],{"className":3944,"code":5568,"language":3946,"meta":502,"style":502},"\u002F**\n * 生成用于数字签名的 RSA 密钥对\n *\u002F\nasync function generateSignKeyPair() {\n    return await crypto.subtle.generateKey(\n        {\n        name: \"RSASSA-PKCS1-v1_5\",\n        modulusLength: 2048,\n        publicExponent: new Uint8Array([1, 0, 1]),\n        hash: \"SHA-256\",\n        },\n        true,\n        [\"sign\", \"verify\"] \u002F\u002F 注意：这里的用途是签名和验证\n    );\n}\n",[42,5570,5571,5575,5580,5584,5597,5615,5619,5633,5644,5671,5686,5690,5696,5721,5727],{"__ignoreMap":502},[688,5572,5573],{"class":690,"line":691},[688,5574,3953],{"class":859},[688,5576,5577],{"class":690,"line":506},[688,5578,5579],{"class":859}," * 生成用于数字签名的 RSA 密钥对\n",[688,5581,5582],{"class":690,"line":535},[688,5583,4010],{"class":859},[688,5585,5586,5588,5590,5593,5595],{"class":690,"line":609},[688,5587,4015],{"class":2260},[688,5589,4018],{"class":2260},[688,5591,5592],{"class":783}," generateSignKeyPair",[688,5594,4056],{"class":791},[688,5596,871],{"class":791},[688,5598,5599,5601,5603,5605,5607,5609,5611,5613],{"class":690,"line":624},[688,5600,4261],{"class":776},[688,5602,4105],{"class":776},[688,5604,4108],{"class":787},[688,5606,2304],{"class":791},[688,5608,4113],{"class":787},[688,5610,2304],{"class":791},[688,5612,4380],{"class":783},[688,5614,4383],{"class":797},[688,5616,5617],{"class":690,"line":856},[688,5618,4388],{"class":791},[688,5620,5621,5623,5625,5627,5629,5631],{"class":690,"line":863},[688,5622,4393],{"class":797},[688,5624,801],{"class":791},[688,5626,2307],{"class":791},[688,5628,3677],{"class":697},[688,5630,2269],{"class":791},[688,5632,838],{"class":791},[688,5634,5635,5638,5640,5642],{"class":690,"line":874},[688,5636,5637],{"class":797},"        modulusLength",[688,5639,801],{"class":791},[688,5641,5150],{"class":2478},[688,5643,838],{"class":791},[688,5645,5646,5649,5651,5653,5655,5657,5659,5661,5663,5665,5667,5669],{"class":690,"line":890},[688,5647,5648],{"class":797},"        publicExponent",[688,5650,801],{"class":791},[688,5652,4050],{"class":791},[688,5654,4168],{"class":783},[688,5656,5169],{"class":797},[688,5658,2739],{"class":2478},[688,5660,4128],{"class":791},[688,5662,5176],{"class":2478},[688,5664,4128],{"class":791},[688,5666,5181],{"class":2478},[688,5668,5184],{"class":797},[688,5670,838],{"class":791},[688,5672,5673,5676,5678,5680,5682,5684],{"class":690,"line":896},[688,5674,5675],{"class":797},"        hash",[688,5677,801],{"class":791},[688,5679,2307],{"class":791},[688,5681,3750],{"class":697},[688,5683,2269],{"class":791},[688,5685,838],{"class":791},[688,5687,5688],{"class":690,"line":905},[688,5689,4422],{"class":791},[688,5691,5692,5694],{"class":690,"line":1334},[688,5693,5216],{"class":4427},[688,5695,838],{"class":791},[688,5697,5698,5700,5702,5705,5707,5709,5711,5714,5716,5718],{"class":690,"line":1343},[688,5699,4438],{"class":797},[688,5701,2269],{"class":791},[688,5703,5704],{"class":697},"sign",[688,5706,2269],{"class":791},[688,5708,4128],{"class":791},[688,5710,2307],{"class":791},[688,5712,5713],{"class":697},"verify",[688,5715,2269],{"class":791},[688,5717,4457],{"class":797},[688,5719,5720],{"class":859},"\u002F\u002F 注意：这里的用途是签名和验证\n",[688,5722,5723,5725],{"class":690,"line":1351},[688,5724,4465],{"class":797},[688,5726,4059],{"class":791},[688,5728,5729],{"class":690,"line":1359},[688,5730,4270],{"class":791},[65,5732,5733],{"start":506},[68,5734,5735],{},[71,5736,5737],{},"使用私钥对数据生成签名",[679,5739,5741],{"className":3944,"code":5740,"language":3946,"meta":502,"style":502},"\n\u002F**\n * 使用私钥对数据生成签名\n * @returns {Promise\u003CArrayBuffer>} 返回签名摘要\n *\u002F\nasync function signData(text, privateKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    return await crypto.subtle.sign(\n        { name: \"RSASSA-PKCS1-v1_5\" },\n        privateKey,\n        data\n    );\n}\n",[42,5742,5743,5747,5751,5756,5773,5777,5798,5814,5836,5840,5858,5874,5880,5884,5890],{"__ignoreMap":502},[688,5744,5745],{"class":690,"line":691},[688,5746,4088],{"emptyLinePlaceholder":1003},[688,5748,5749],{"class":690,"line":506},[688,5750,3953],{"class":859},[688,5752,5753],{"class":690,"line":535},[688,5754,5755],{"class":859}," * 使用私钥对数据生成签名\n",[688,5757,5758,5760,5762,5764,5766,5768,5770],{"class":690,"line":609},[688,5759,3963],{"class":859},[688,5761,3966],{"class":776},[688,5763,3995],{"class":3969},[688,5765,3973],{"class":776},[688,5767,3484],{"class":3976},[688,5769,908],{"class":776},[688,5771,5772],{"class":859}," 返回签名摘要\n",[688,5774,5775],{"class":690,"line":624},[688,5776,4010],{"class":859},[688,5778,5779,5781,5783,5786,5788,5790,5792,5794,5796],{"class":690,"line":856},[688,5780,4015],{"class":2260},[688,5782,4018],{"class":2260},[688,5784,5785],{"class":783}," signData",[688,5787,788],{"class":791},[688,5789,4026],{"class":3982},[688,5791,4128],{"class":791},[688,5793,5450],{"class":3982},[688,5795,4029],{"class":791},[688,5797,871],{"class":791},[688,5799,5800,5802,5804,5806,5808,5810,5812],{"class":690,"line":863},[688,5801,4041],{"class":2260},[688,5803,4044],{"class":787},[688,5805,4047],{"class":791},[688,5807,4050],{"class":791},[688,5809,4053],{"class":783},[688,5811,4056],{"class":797},[688,5813,4059],{"class":791},[688,5815,5816,5818,5820,5822,5824,5826,5828,5830,5832,5834],{"class":690,"line":874},[688,5817,4041],{"class":2260},[688,5819,4066],{"class":787},[688,5821,4047],{"class":791},[688,5823,4044],{"class":787},[688,5825,2304],{"class":791},[688,5827,4075],{"class":783},[688,5829,788],{"class":797},[688,5831,4026],{"class":787},[688,5833,4029],{"class":797},[688,5835,4059],{"class":791},[688,5837,5838],{"class":690,"line":890},[688,5839,4088],{"emptyLinePlaceholder":1003},[688,5841,5842,5844,5846,5848,5850,5852,5854,5856],{"class":690,"line":896},[688,5843,4261],{"class":776},[688,5845,4105],{"class":776},[688,5847,4108],{"class":787},[688,5849,2304],{"class":791},[688,5851,4113],{"class":787},[688,5853,2304],{"class":791},[688,5855,5704],{"class":783},[688,5857,4383],{"class":797},[688,5859,5860,5862,5864,5866,5868,5870,5872],{"class":690,"line":905},[688,5861,5371],{"class":791},[688,5863,5374],{"class":797},[688,5865,801],{"class":791},[688,5867,2307],{"class":791},[688,5869,3677],{"class":697},[688,5871,2269],{"class":791},[688,5873,5385],{"class":791},[688,5875,5876,5878],{"class":690,"line":1334},[688,5877,5497],{"class":787},[688,5879,838],{"class":791},[688,5881,5882],{"class":690,"line":1343},[688,5883,5397],{"class":787},[688,5885,5886,5888],{"class":690,"line":1351},[688,5887,4465],{"class":797},[688,5889,4059],{"class":791},[688,5891,5892],{"class":690,"line":1359},[688,5893,4270],{"class":791},[65,5895,5896],{"start":535},[68,5897,5898],{},[71,5899,5900],{},"公钥验证签名是否有效",[679,5902,5904],{"className":3944,"code":5903,"language":3946,"meta":502,"style":502},"\n\u002F**\n * 使用公钥验证签名是否有效\n * @returns {Promise\u003Cboolean>} 验证成功返回 true，被篡改则返回 false\n *\u002F\nasync function verifyData(text, signature, publicKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    return await crypto.subtle.verify(\n        { name: \"RSASSA-PKCS1-v1_5\" },\n        publicKey,\n        signature, \u002F\u002F 之前生成的签名 ArrayBuffer\n        data \u002F\u002F 原始数据\n    );\n}\n",[42,5905,5906,5910,5914,5919,5937,5941,5967,5983,6005,6009,6027,6043,6049,6059,6067,6073],{"__ignoreMap":502},[688,5907,5908],{"class":690,"line":691},[688,5909,4088],{"emptyLinePlaceholder":1003},[688,5911,5912],{"class":690,"line":506},[688,5913,3953],{"class":859},[688,5915,5916],{"class":690,"line":535},[688,5917,5918],{"class":859}," * 使用公钥验证签名是否有效\n",[688,5920,5921,5923,5925,5927,5929,5932,5934],{"class":690,"line":609},[688,5922,3963],{"class":859},[688,5924,3966],{"class":776},[688,5926,3995],{"class":3969},[688,5928,3973],{"class":776},[688,5930,5931],{"class":3976},"Promise\u003Cboolean>",[688,5933,908],{"class":776},[688,5935,5936],{"class":859}," 验证成功返回 true，被篡改则返回 false\n",[688,5938,5939],{"class":690,"line":624},[688,5940,4010],{"class":859},[688,5942,5943,5945,5947,5950,5952,5954,5956,5959,5961,5963,5965],{"class":690,"line":856},[688,5944,4015],{"class":2260},[688,5946,4018],{"class":2260},[688,5948,5949],{"class":783}," verifyData",[688,5951,788],{"class":791},[688,5953,4026],{"class":3982},[688,5955,4128],{"class":791},[688,5957,5958],{"class":3982}," signature",[688,5960,4128],{"class":791},[688,5962,5297],{"class":3982},[688,5964,4029],{"class":791},[688,5966,871],{"class":791},[688,5968,5969,5971,5973,5975,5977,5979,5981],{"class":690,"line":863},[688,5970,4041],{"class":2260},[688,5972,4044],{"class":787},[688,5974,4047],{"class":791},[688,5976,4050],{"class":791},[688,5978,4053],{"class":783},[688,5980,4056],{"class":797},[688,5982,4059],{"class":791},[688,5984,5985,5987,5989,5991,5993,5995,5997,5999,6001,6003],{"class":690,"line":874},[688,5986,4041],{"class":2260},[688,5988,4066],{"class":787},[688,5990,4047],{"class":791},[688,5992,4044],{"class":787},[688,5994,2304],{"class":791},[688,5996,4075],{"class":783},[688,5998,788],{"class":797},[688,6000,4026],{"class":787},[688,6002,4029],{"class":797},[688,6004,4059],{"class":791},[688,6006,6007],{"class":690,"line":890},[688,6008,4088],{"emptyLinePlaceholder":1003},[688,6010,6011,6013,6015,6017,6019,6021,6023,6025],{"class":690,"line":896},[688,6012,4261],{"class":776},[688,6014,4105],{"class":776},[688,6016,4108],{"class":787},[688,6018,2304],{"class":791},[688,6020,4113],{"class":787},[688,6022,2304],{"class":791},[688,6024,5713],{"class":783},[688,6026,4383],{"class":797},[688,6028,6029,6031,6033,6035,6037,6039,6041],{"class":690,"line":905},[688,6030,5371],{"class":791},[688,6032,5374],{"class":797},[688,6034,801],{"class":791},[688,6036,2307],{"class":791},[688,6038,3677],{"class":697},[688,6040,2269],{"class":791},[688,6042,5385],{"class":791},[688,6044,6045,6047],{"class":690,"line":1334},[688,6046,5390],{"class":787},[688,6048,838],{"class":791},[688,6050,6051,6054,6056],{"class":690,"line":1343},[688,6052,6053],{"class":787},"        signature",[688,6055,4128],{"class":791},[688,6057,6058],{"class":859}," \u002F\u002F 之前生成的签名 ArrayBuffer\n",[688,6060,6061,6064],{"class":690,"line":1351},[688,6062,6063],{"class":787},"        data",[688,6065,6066],{"class":859}," \u002F\u002F 原始数据\n",[688,6068,6069,6071],{"class":690,"line":1359},[688,6070,4465],{"class":797},[688,6072,4059],{"class":791},[688,6074,6075],{"class":690,"line":1367},[688,6076,4270],{"class":791},[123,6078,6080],{"id":6079},"_74-密钥的导入与导出-import-export","7.4 密钥的导入与导出 (Import \u002F Export)",[13,6082,6083],{},"在实际业务中，密钥不能只存在于浏览器内存中，我们需要将其导出为字符串传给后端，或者将后端的字符串密钥导入为浏览器可识别的 CryptoKey 对象。",[13,6085,6086],{},"Web Crypto 支持多种格式，最常用的是 JWK (JSON Web Key) 格式（易读、跨平台）和 raw\u002Fspki\u002Fpkcs8（二进制字节流）。",[65,6088,6089],{},[68,6090,6091],{},[71,6092,6093],{},"导出密钥为 JSON (JWK 格式)",[679,6095,6097],{"className":3944,"code":6096,"language":3946,"meta":502,"style":502},"\u002F**\n * 将 CryptoKey 密钥对象导出为标准的 JSON 对象 (JWK)\n * @param {CryptoKey} key \n * @returns {Promise\u003CObject>} 可直接用于传输或 JSON.stringify 的对象\n *\u002F\nasync function exportKeyToJWK(key) {\n    return await crypto.subtle.exportKey(\"jwk\", key);\n}\n",[42,6098,6099,6103,6108,6126,6144,6148,6165,6199],{"__ignoreMap":502},[688,6100,6101],{"class":690,"line":691},[688,6102,3953],{"class":859},[688,6104,6105],{"class":690,"line":506},[688,6106,6107],{"class":859}," * 将 CryptoKey 密钥对象导出为标准的 JSON 对象 (JWK)\n",[688,6109,6110,6112,6114,6116,6118,6120,6122,6124],{"class":690,"line":535},[688,6111,3963],{"class":859},[688,6113,3966],{"class":776},[688,6115,3970],{"class":3969},[688,6117,3973],{"class":776},[688,6119,3490],{"class":3976},[688,6121,908],{"class":776},[688,6123,4529],{"class":3982},[688,6125,2482],{"class":859},[688,6127,6128,6130,6132,6134,6136,6139,6141],{"class":690,"line":609},[688,6129,3963],{"class":859},[688,6131,3966],{"class":776},[688,6133,3995],{"class":3969},[688,6135,3973],{"class":776},[688,6137,6138],{"class":3976},"Promise\u003CObject>",[688,6140,908],{"class":776},[688,6142,6143],{"class":859}," 可直接用于传输或 JSON.stringify 的对象\n",[688,6145,6146],{"class":690,"line":624},[688,6147,4010],{"class":859},[688,6149,6150,6152,6154,6157,6159,6161,6163],{"class":690,"line":856},[688,6151,4015],{"class":2260},[688,6153,4018],{"class":2260},[688,6155,6156],{"class":783}," exportKeyToJWK",[688,6158,788],{"class":791},[688,6160,467],{"class":3982},[688,6162,4029],{"class":791},[688,6164,871],{"class":791},[688,6166,6167,6169,6171,6173,6175,6177,6179,6182,6184,6186,6189,6191,6193,6195,6197],{"class":690,"line":863},[688,6168,4261],{"class":776},[688,6170,4105],{"class":776},[688,6172,4108],{"class":787},[688,6174,2304],{"class":791},[688,6176,4113],{"class":787},[688,6178,2304],{"class":791},[688,6180,6181],{"class":783},"exportKey",[688,6183,788],{"class":797},[688,6185,2269],{"class":791},[688,6187,6188],{"class":697},"jwk",[688,6190,2269],{"class":791},[688,6192,4128],{"class":791},[688,6194,4529],{"class":787},[688,6196,4029],{"class":797},[688,6198,4059],{"class":791},[688,6200,6201],{"class":690,"line":874},[688,6202,4270],{"class":791},[65,6204,6205],{"start":506},[68,6206,6207],{},[71,6208,6209],{},"将 JSON 导入回密钥对象",[679,6211,6213],{"className":3944,"code":6212,"language":3946,"meta":502,"style":502},"\u002F**\n * 将 JWK 格式的 JSON 对象导入还原为可用的 AES-GCM 密钥\n * @param {Object} jwkObject \n * @returns {Promise\u003CCryptoKey>}\n *\u002F\nasync function importKeyFromJWK(jwkObject) {\n    return await crypto.subtle.importKey(\n        \"jwk\",\n        jwkObject,\n        { name: \"AES-GCM\" }, \u002F\u002F 必须指定对应的算法\n        true, \u002F\u002F 是否允许再次导出\n        [\"encrypt\", \"decrypt\"] \u002F\u002F 该密钥允许的用途\n    );\n}\n",[42,6214,6215,6219,6224,6244,6258,6262,6280,6299,6310,6317,6337,6346,6369,6375],{"__ignoreMap":502},[688,6216,6217],{"class":690,"line":691},[688,6218,3953],{"class":859},[688,6220,6221],{"class":690,"line":506},[688,6222,6223],{"class":859}," * 将 JWK 格式的 JSON 对象导入还原为可用的 AES-GCM 密钥\n",[688,6225,6226,6228,6230,6232,6234,6237,6239,6242],{"class":690,"line":535},[688,6227,3963],{"class":859},[688,6229,3966],{"class":776},[688,6231,3970],{"class":3969},[688,6233,3973],{"class":776},[688,6235,6236],{"class":3976},"Object",[688,6238,908],{"class":776},[688,6240,6241],{"class":3982}," jwkObject",[688,6243,2482],{"class":859},[688,6245,6246,6248,6250,6252,6254,6256],{"class":690,"line":609},[688,6247,3963],{"class":859},[688,6249,3966],{"class":776},[688,6251,3995],{"class":3969},[688,6253,3973],{"class":776},[688,6255,4341],{"class":3976},[688,6257,4270],{"class":776},[688,6259,6260],{"class":690,"line":624},[688,6261,4010],{"class":859},[688,6263,6264,6266,6268,6271,6273,6276,6278],{"class":690,"line":856},[688,6265,4015],{"class":2260},[688,6267,4018],{"class":2260},[688,6269,6270],{"class":783}," importKeyFromJWK",[688,6272,788],{"class":791},[688,6274,6275],{"class":3982},"jwkObject",[688,6277,4029],{"class":791},[688,6279,871],{"class":791},[688,6281,6282,6284,6286,6288,6290,6292,6294,6297],{"class":690,"line":863},[688,6283,4261],{"class":776},[688,6285,4105],{"class":776},[688,6287,4108],{"class":787},[688,6289,2304],{"class":791},[688,6291,4113],{"class":787},[688,6293,2304],{"class":791},[688,6295,6296],{"class":783},"importKey",[688,6298,4383],{"class":797},[688,6300,6301,6304,6306,6308],{"class":690,"line":874},[688,6302,6303],{"class":791},"        \"",[688,6305,6188],{"class":697},[688,6307,2269],{"class":791},[688,6309,838],{"class":791},[688,6311,6312,6315],{"class":690,"line":890},[688,6313,6314],{"class":787},"        jwkObject",[688,6316,838],{"class":791},[688,6318,6319,6321,6323,6325,6327,6329,6331,6334],{"class":690,"line":896},[688,6320,5371],{"class":791},[688,6322,5374],{"class":797},[688,6324,801],{"class":791},[688,6326,2307],{"class":791},[688,6328,4400],{"class":697},[688,6330,2269],{"class":791},[688,6332,6333],{"class":791}," },",[688,6335,6336],{"class":859}," \u002F\u002F 必须指定对应的算法\n",[688,6338,6339,6341,6343],{"class":690,"line":905},[688,6340,5216],{"class":4427},[688,6342,4128],{"class":791},[688,6344,6345],{"class":859}," \u002F\u002F 是否允许再次导出\n",[688,6347,6348,6350,6352,6354,6356,6358,6360,6362,6364,6366],{"class":690,"line":1334},[688,6349,4438],{"class":797},[688,6351,2269],{"class":791},[688,6353,4443],{"class":697},[688,6355,2269],{"class":791},[688,6357,4128],{"class":791},[688,6359,2307],{"class":791},[688,6361,4452],{"class":697},[688,6363,2269],{"class":791},[688,6365,4457],{"class":797},[688,6367,6368],{"class":859},"\u002F\u002F 该密钥允许的用途\n",[688,6370,6371,6373],{"class":690,"line":1343},[688,6372,4465],{"class":797},[688,6374,4059],{"class":791},[688,6376,6377],{"class":690,"line":1351},[688,6378,4270],{"class":791},[17,6380,6382],{"id":6381},"_8-避坑指南","8 避坑指南",[65,6384,6385],{},[68,6386,6387],{},[71,6388,6389],{},"陷阱一：IV 重复使用",[128,6391,6392,6398],{},[68,6393,6394,6397],{},[71,6395,6396],{},"问题："," 在 AES-GCM 等对称加密中，重复使用相同的初始化向量（IV）会导致密钥流重复，严重破坏加密安全性。",[68,6399,6400,6403],{},[71,6401,6402],{},"正确做法：",[128,6404,6405,6408,6411,6414],{},[68,6406,6407],{},"每次加密都使用 crypto.getRandomValues() 生成新的随机 IV",[68,6409,6410],{},"AES-GCM 模式推荐使用 12 字节（非 16 字节！）的 IV",[68,6412,6413],{},"同一密钥下 IV 重复概率应低于 2⁻³²",[68,6415,6416],{},"IV 不需要保密，但必须唯一——记得将 IV 与密文一起存储",[65,6418,6419],{"start":506},[68,6420,6421],{},[71,6422,6423],{},"陷阱二：密钥管理不当",[128,6425,6426,6442],{},[68,6427,6428,6431],{},[71,6429,6430],{},"常见错误：",[128,6432,6433,6436,6439],{},[68,6434,6435],{},"将密钥存储在 localStorage 或 Cookie 中",[68,6437,6438],{},"使用不安全的密钥传输方式",[68,6440,6441],{},"未设置适当的密钥使用限制",[68,6443,6444,6447],{},[71,6445,6446],{},"安全建议：",[128,6448,6449,6452,6455],{},[68,6450,6451],{},"敏感会话密钥存储在 Web Worker 内存中",[68,6453,6454],{},"需要持久化的密钥使用 IndexedDB",[68,6456,6457],{},"使用 exportKey 和 importKey 安全地传输公钥",[65,6459,6460],{"start":535},[68,6461,6462],{},[71,6463,6464],{},"陷阱三：算法选择失误",[128,6466,6467,6470,6473],{},[68,6468,6469],{},"不要使用 SHA-1 或 MD5 进行哈希",[68,6471,6472],{},"不要使用 ECB 模式的 AES 加密",[68,6474,6475],{},"RSA 密钥长度至少 2048 位",[17,6477,6479],{"id":6478},"_9-最佳实践","9 最佳实践",[65,6481,6482],{},[68,6483,6484],{},[71,6485,6486],{},"使用方式选型指南推荐",[13,6488,6489],{},"自 Node.js 19+ 起，Web Crypto API 已成为内置标准，可通过以下方式访问：",[679,6491,6493],{"className":3944,"code":6492,"language":3946,"meta":502,"style":502},"\u002F\u002F 方式一：使用 globalThis\nconst { subtle } = globalThis.crypto;\n\n\u002F\u002F 方式二：使用 Node.js 模块\nconst { webcrypto } = require('node:crypto');\nconst { subtle } = webcrypto;\n",[42,6494,6495,6500,6523,6527,6532,6561],{"__ignoreMap":502},[688,6496,6497],{"class":690,"line":691},[688,6498,6499],{"class":859},"\u002F\u002F 方式一：使用 globalThis\n",[688,6501,6502,6504,6506,6509,6511,6513,6516,6518,6521],{"class":690,"line":506},[688,6503,4284],{"class":2260},[688,6505,3973],{"class":791},[688,6507,6508],{"class":787}," subtle ",[688,6510,908],{"class":791},[688,6512,4047],{"class":791},[688,6514,6515],{"class":787}," globalThis",[688,6517,2304],{"class":791},[688,6519,6520],{"class":787},"crypto",[688,6522,4059],{"class":791},[688,6524,6525],{"class":690,"line":535},[688,6526,4088],{"emptyLinePlaceholder":1003},[688,6528,6529],{"class":690,"line":609},[688,6530,6531],{"class":859},"\u002F\u002F 方式二：使用 Node.js 模块\n",[688,6533,6534,6536,6538,6541,6543,6545,6548,6550,6552,6555,6557,6559],{"class":690,"line":624},[688,6535,4284],{"class":2260},[688,6537,3973],{"class":791},[688,6539,6540],{"class":787}," webcrypto ",[688,6542,908],{"class":791},[688,6544,4047],{"class":791},[688,6546,6547],{"class":783}," require",[688,6549,788],{"class":787},[688,6551,807],{"class":791},[688,6553,6554],{"class":697},"node:crypto",[688,6556,807],{"class":791},[688,6558,4029],{"class":787},[688,6560,4059],{"class":791},[688,6562,6563,6565,6567,6569,6571,6573,6576],{"class":690,"line":856},[688,6564,4284],{"class":2260},[688,6566,3973],{"class":791},[688,6568,6508],{"class":787},[688,6570,908],{"class":791},[688,6572,4047],{"class":791},[688,6574,6575],{"class":787}," webcrypto",[688,6577,4059],{"class":791},[13,6579,6580],{},"这两种方式虽然最终都能让你获取到 subtle 接口，但它们在设计哲学、模块机制、环境通用性以及底层依赖上有着本质的区别。",[260,6582,6583,6596],{},[263,6584,6585],{},[266,6586,6587,6590,6593],{},[269,6588,6589],{},"对比维度",[269,6591,6592],{},"方式一：globalThis.crypto",[269,6594,6595],{},"方式二：require('node:crypto')",[278,6597,6598,6609,6620,6631,6642],{},[266,6599,6600,6603,6606],{},[283,6601,6602],{},"本质定位",[283,6604,6605],{},"Web标准API（与浏览器对齐）",[283,6607,6608],{},"Node.js原生模块API（特定服务端）",[266,6610,6611,6614,6617],{},[283,6612,6613],{},"引入方式",[283,6615,6616],{},"零依赖，直接全局访问",[283,6618,6619],{},"需要显式通过require引入模块",[266,6621,6622,6625,6628],{},[283,6623,6624],{},"跨端通用性",[283,6626,6627],{},"极高（同构JavaScript）",[283,6629,6630],{},"极低（只能在Node.js运行）",[266,6632,6633,6636,6639],{},[283,6634,6635],{},"底层实现",[283,6637,6638],{},"浏览器内核（前端）\u002F Node V8底层封装（后端）",[283,6640,6641],{},"强依赖Node.js内置的OpenSSL库",[266,6643,6644,6647,6650],{},[283,6645,6646],{},"执行上下文",[283,6648,6649],{},"必须在安全上下文（前端限制）",[283,6651,6652],{},"服务端环境，无任何安全上下文限制",[13,6654,6655,6658],{},[71,6656,6657],{},"推荐使用\"globalThis.crypto\"的场景："," 正在编写纯前端项目、前后端全栈通用工具库（Isomorphic Lib），或者代码需要部署在 Cloudflare Workers、Vercel Edge 等现代边缘计算架构中。",[13,6660,6661,6664],{},[71,6662,6663],{},"推荐使用\"require('node:crypto')\"的场景："," 正在编写纯 Node.js 服务端项目（如大型后端服务、CLI 工具），且除了 Web Crypto 之外，还需要用到 Node.js 特有的高级安全特性（如流式加密大文件、硬件安全模块 HSM 交互等）。",[65,6666,6667],{"start":506},[68,6668,6669],{},[71,6670,6671],{},"使用细节建议",[13,6673,6674],{},"Web Crypto API 是浏览器原生加密的黄金标准。它以零依赖、高性能、高安全性的优势，正在逐步取代各种第三方加密库。然而，正如其名 \"subtle\" 所警示的，加密是一个极其专业的领域，细微的错误就可能导致整个系统安全性完全失效。在使用 Web Crypto API 时：",[128,6676,6677,6680,6683],{},[68,6678,6679],{},"始终在 HTTPS 环境下使用",[68,6681,6682],{},"选择正确的算法和参数（如 AES-GCM + 12 字节 IV）",[68,6684,6685],{},"妥善管理密钥的生命周期",[17,6687,6689],{"id":6688},"_10-加解密模板工具","10 加解密模板工具",[13,6691,6692],{},"以下为您编写的这一套 AES-GCM 高级对称加密与解密工具函数。它做到了100% 零依赖、纯原生，通过 globalThis.crypto 实现完美同构。你不需要进行任何环境判断，同一段代码可以无缝运行在浏览器（安全上下文）、Node.js（19+）、Deno、Bun 以及边缘计算（如 Cloudflare Workers）中。",[13,6694,6695],{},"为了解决数据传输与存储的问题，工具函数内部自动处理了 ArrayBuffer 与 Base64 字符串之间的相互转换。可以直接复制使用，代码如下：",[679,6697,6700],{"className":766,"code":6698,"filename":6699,"language":769,"meta":502,"style":502},"\u002F**\n * 完美的同构 Web Crypto 安全加解密工具库 (TypeScript 箭头函数版本)\n * 适用环境：Browser (HTTPS), Node.js 19+, Deno, Bun, Cloudflare Workers\n *\u002F\n\n\u002F\u002F 1. 获取全局同构的 subtle 对象\nconst cryptoProvider = globalThis.crypto\nif (!cryptoProvider) {\n    throw new Error('当前运行环境不支持 Web Crypto API。请确保在 HTTPS、localhost 或现代 Node.js 19+ 环境下运行。')\n}\nconst subtle: SubtleCrypto = cryptoProvider.subtle\n\n\u002F**\n * 辅助函数：将 ArrayBuffer 转换为可传输的 Base64 字符串\n * 使用现代批处理写法apply，100% 消除严格模式下的 undefined 类型报错\n *\u002F\nconst bufferToBase64 = (buffer: ArrayBuffer): string => {\n    const bytes = new Uint8Array(buffer)\n    const binary = String.fromCharCode.apply(null, bytes as unknown as number[])\n    return btoa(binary)\n}\n\n\u002F**\n * 辅助函数：将 Base64 字符串还原为 ArrayBuffer\n * 使用 Array.from 映射，确保严格类型安全\n *\u002F\nconst base64ToBuffer = (base64: string): ArrayBuffer => {\n    const binaryString = atob(base64)\n    const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0))\n    return bytes.buffer\n}\n\n\u002F**\n * [同构] 生成 AES-GCM 256位加密密钥\n *\u002F\nexport const generateAESKey = async (): Promise\u003CCryptoKey> => {\n    return await subtle.generateKey(\n        { name: 'AES-GCM', length: 256 },\n        true, \u002F\u002F 允许导出\n        ['encrypt', 'decrypt']\n    )\n}\n\n\u002F**\n * [同构] 导出 CryptoKey 密钥为可传输的 Base64 字符串\n * @param cryptoKey - 浏览器或服务端生成的 CryptoKey 实体\n *\u002F\nexport const exportKeyToBase64 = async (cryptoKey: CryptoKey): Promise\u003Cstring> => {\n    const exported = await subtle.exportKey('raw', cryptoKey)\n    return bufferToBase64(exported)\n}\n\n\u002F**\n * [同构] 从 Base64 字符串导入还原为 CryptoKey 密钥对象\n * @param base64Key - Base64 格式的密钥字符串\n *\u002F\nexport const importKeyFromBase64 = async (base64Key: string): Promise\u003CCryptoKey> => {\n    const rawKeyBuffer = base64ToBuffer(base64Key)\n    return await subtle.importKey('raw', rawKeyBuffer, { name: 'AES-GCM' }, true, ['encrypt', 'decrypt'])\n}\n\n\u002F**\n * [同构] 使用 AES-GCM 算法加密字符串文本\n * @param text - 待加密的明文文本\n * @param key - CryptoKey 密钥对象\n * @returns 返回打包了 \"IV\" 和 \"密文\" 的统一 Base64 字符串\n *\u002F\nexport const encryptText = async (text: string, key: CryptoKey): Promise\u003Cstring> => {\n    const encoder = new TextEncoder()\n    const encodedText: Uint8Array\u003CArrayBuffer> = encoder.encode(text)\n    \u002F\u002F 生成 12 字节的密码学安全伪随机数作为 IV (AES-GCM 标准推荐)\n    const iv: Uint8Array\u003CArrayBuffer> = cryptoProvider.getRandomValues(new Uint8Array(12))\n    \u002F\u002F 执行加密，返回密文 ArrayBuffer\n    const cipherBuffer: ArrayBuffer = await subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, encodedText)\n    \u002F\u002F 将 IV（12字节）和密文拼接，方便单字符串传输\n    const combined = new Uint8Array(iv.length + cipherBuffer.byteLength)\n    combined.set(iv, 0)\n    combined.set(new Uint8Array(cipherBuffer), iv.length)\n    return bufferToBase64(combined.buffer)\n}\n\n\u002F**\n * [同构] 使用 AES-GCM 算法解密文本\n * @param combinedBase64 - 由 encryptText 生成的统一加密 Base64 字符串（内部含 IV）\n * @param key - 对应的 CryptoKey 密钥对象\n *\u002F\nexport const decryptText = async (combinedBase64: string, key: CryptoKey): Promise\u003Cstring> => {\n    const combinedBuffer = base64ToBuffer(combinedBase64)\n    const combinedBytes = new Uint8Array(combinedBuffer)\n    \u002F\u002F 从头部切出前 12 字节作为 IV\n    const iv: Uint8Array\u003CArrayBuffer> = combinedBytes.slice(0, 12)\n    \u002F\u002F 剩下的部分作为真正的密文\n    const cipherText: Uint8Array\u003CArrayBuffer> = combinedBytes.slice(12)\n    \u002F\u002F 执行解密\n    const decryptedBuffer: ArrayBuffer = await subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, cipherText.buffer)\n    const decoder = new TextDecoder()\n    return decoder.decode(decryptedBuffer)\n}\n","crypto.ts",[42,6701,6702,6706,6711,6716,6720,6724,6729,6745,6761,6782,6786,6808,6812,6816,6821,6826,6830,6859,6878,6921,6935,6939,6943,6947,6952,6957,6961,6987,7005,7050,7061,7066,7071,7076,7082,7087,7120,7135,7161,7171,7192,7198,7203,7208,7213,7219,7234,7239,7277,7310,7325,7330,7335,7340,7346,7361,7366,7403,7422,7488,7493,7498,7503,7509,7523,7537,7549,7554,7598,7614,7645,7651,7688,7694,7749,7755,7791,7812,7844,7862,7867,7872,7877,7883,7898,7912,7917,7962,7980,8001,8007,8044,8050,8081,8086,8143,8158,8175],{"__ignoreMap":502},[688,6703,6704],{"class":690,"line":691},[688,6705,3953],{"class":859},[688,6707,6708],{"class":690,"line":506},[688,6709,6710],{"class":859}," * 完美的同构 Web Crypto 安全加解密工具库 (TypeScript 箭头函数版本)\n",[688,6712,6713],{"class":690,"line":535},[688,6714,6715],{"class":859}," * 适用环境：Browser (HTTPS), Node.js 19+, Deno, Bun, Cloudflare Workers\n",[688,6717,6718],{"class":690,"line":609},[688,6719,4010],{"class":859},[688,6721,6722],{"class":690,"line":624},[688,6723,4088],{"emptyLinePlaceholder":1003},[688,6725,6726],{"class":690,"line":856},[688,6727,6728],{"class":859},"\u002F\u002F 1. 获取全局同构的 subtle 对象\n",[688,6730,6731,6733,6736,6738,6740,6742],{"class":690,"line":863},[688,6732,4284],{"class":2260},[688,6734,6735],{"class":787}," cryptoProvider ",[688,6737,2266],{"class":791},[688,6739,6515],{"class":787},[688,6741,2304],{"class":791},[688,6743,6744],{"class":787},"crypto\n",[688,6746,6747,6750,6753,6756,6759],{"class":690,"line":874},[688,6748,6749],{"class":776},"if",[688,6751,6752],{"class":787}," (",[688,6754,6755],{"class":791},"!",[688,6757,6758],{"class":787},"cryptoProvider) ",[688,6760,792],{"class":791},[688,6762,6763,6766,6768,6771,6773,6775,6778,6780],{"class":690,"line":890},[688,6764,6765],{"class":776},"    throw",[688,6767,4050],{"class":791},[688,6769,6770],{"class":783}," Error",[688,6772,788],{"class":797},[688,6774,807],{"class":791},[688,6776,6777],{"class":697},"当前运行环境不支持 Web Crypto API。请确保在 HTTPS、localhost 或现代 Node.js 19+ 环境下运行。",[688,6779,807],{"class":791},[688,6781,911],{"class":797},[688,6783,6784],{"class":690,"line":896},[688,6785,4270],{"class":791},[688,6787,6788,6790,6793,6795,6798,6800,6803,6805],{"class":690,"line":905},[688,6789,4284],{"class":2260},[688,6791,6792],{"class":787}," subtle",[688,6794,801],{"class":791},[688,6796,6797],{"class":694}," SubtleCrypto",[688,6799,4047],{"class":791},[688,6801,6802],{"class":787}," cryptoProvider",[688,6804,2304],{"class":791},[688,6806,6807],{"class":787},"subtle\n",[688,6809,6810],{"class":690,"line":1334},[688,6811,4088],{"emptyLinePlaceholder":1003},[688,6813,6814],{"class":690,"line":1343},[688,6815,3953],{"class":859},[688,6817,6818],{"class":690,"line":1351},[688,6819,6820],{"class":859}," * 辅助函数：将 ArrayBuffer 转换为可传输的 Base64 字符串\n",[688,6822,6823],{"class":690,"line":1359},[688,6824,6825],{"class":859}," * 使用现代批处理写法apply，100% 消除严格模式下的 undefined 类型报错\n",[688,6827,6828],{"class":690,"line":1367},[688,6829,4010],{"class":859},[688,6831,6832,6834,6837,6839,6841,6844,6846,6849,6852,6855,6857],{"class":690,"line":1375},[688,6833,4284],{"class":2260},[688,6835,6836],{"class":787}," bufferToBase64 ",[688,6838,2266],{"class":791},[688,6840,6752],{"class":791},[688,6842,6843],{"class":3982},"buffer",[688,6845,801],{"class":791},[688,6847,6848],{"class":694}," ArrayBuffer",[688,6850,6851],{"class":791},"):",[688,6853,6854],{"class":694}," string",[688,6856,4202],{"class":2260},[688,6858,871],{"class":791},[688,6860,6861,6863,6866,6868,6870,6872,6874,6876],{"class":690,"line":1383},[688,6862,4041],{"class":2260},[688,6864,6865],{"class":787}," bytes",[688,6867,4047],{"class":791},[688,6869,4050],{"class":791},[688,6871,4168],{"class":783},[688,6873,788],{"class":797},[688,6875,6843],{"class":787},[688,6877,911],{"class":797},[688,6879,6880,6882,6885,6887,6890,6892,6895,6897,6900,6902,6905,6907,6910,6913,6915,6918],{"class":690,"line":1392},[688,6881,4041],{"class":2260},[688,6883,6884],{"class":787}," binary",[688,6886,4047],{"class":791},[688,6888,6889],{"class":787}," String",[688,6891,2304],{"class":791},[688,6893,6894],{"class":787},"fromCharCode",[688,6896,2304],{"class":791},[688,6898,6899],{"class":783},"apply",[688,6901,788],{"class":797},[688,6903,6904],{"class":791},"null,",[688,6906,6865],{"class":787},[688,6908,6909],{"class":776}," as",[688,6911,6912],{"class":694}," unknown",[688,6914,6909],{"class":776},[688,6916,6917],{"class":694}," number",[688,6919,6920],{"class":797},"[])\n",[688,6922,6923,6925,6928,6930,6933],{"class":690,"line":1400},[688,6924,4261],{"class":776},[688,6926,6927],{"class":783}," btoa",[688,6929,788],{"class":797},[688,6931,6932],{"class":787},"binary",[688,6934,911],{"class":797},[688,6936,6937],{"class":690,"line":1408},[688,6938,4270],{"class":791},[688,6940,6941],{"class":690,"line":1416},[688,6942,4088],{"emptyLinePlaceholder":1003},[688,6944,6945],{"class":690,"line":1425},[688,6946,3953],{"class":859},[688,6948,6949],{"class":690,"line":1434},[688,6950,6951],{"class":859}," * 辅助函数：将 Base64 字符串还原为 ArrayBuffer\n",[688,6953,6954],{"class":690,"line":1443},[688,6955,6956],{"class":859}," * 使用 Array.from 映射，确保严格类型安全\n",[688,6958,6959],{"class":690,"line":1452},[688,6960,4010],{"class":859},[688,6962,6963,6965,6968,6970,6972,6975,6977,6979,6981,6983,6985],{"class":690,"line":1461},[688,6964,4284],{"class":2260},[688,6966,6967],{"class":787}," base64ToBuffer ",[688,6969,2266],{"class":791},[688,6971,6752],{"class":791},[688,6973,6974],{"class":3982},"base64",[688,6976,801],{"class":791},[688,6978,6854],{"class":694},[688,6980,6851],{"class":791},[688,6982,6848],{"class":694},[688,6984,4202],{"class":2260},[688,6986,871],{"class":791},[688,6988,6989,6991,6994,6996,6999,7001,7003],{"class":690,"line":1470},[688,6990,4041],{"class":2260},[688,6992,6993],{"class":787}," binaryString",[688,6995,4047],{"class":791},[688,6997,6998],{"class":783}," atob",[688,7000,788],{"class":797},[688,7002,6974],{"class":787},[688,7004,911],{"class":797},[688,7006,7007,7009,7011,7013,7015,7017,7019,7021,7024,7026,7028,7031,7033,7035,7038,7040,7043,7045,7047],{"class":690,"line":1479},[688,7008,4041],{"class":2260},[688,7010,6865],{"class":787},[688,7012,4047],{"class":791},[688,7014,4168],{"class":787},[688,7016,2304],{"class":791},[688,7018,4160],{"class":783},[688,7020,788],{"class":797},[688,7022,7023],{"class":787},"binaryString",[688,7025,4128],{"class":791},[688,7027,6752],{"class":791},[688,7029,7030],{"class":3982},"char",[688,7032,4029],{"class":791},[688,7034,4202],{"class":2260},[688,7036,7037],{"class":787}," char",[688,7039,2304],{"class":791},[688,7041,7042],{"class":783},"charCodeAt",[688,7044,788],{"class":797},[688,7046,2499],{"class":2478},[688,7048,7049],{"class":797},"))\n",[688,7051,7052,7054,7056,7058],{"class":690,"line":1488},[688,7053,4261],{"class":776},[688,7055,6865],{"class":787},[688,7057,2304],{"class":791},[688,7059,7060],{"class":787},"buffer\n",[688,7062,7064],{"class":690,"line":7063},31,[688,7065,4270],{"class":791},[688,7067,7069],{"class":690,"line":7068},32,[688,7070,4088],{"emptyLinePlaceholder":1003},[688,7072,7074],{"class":690,"line":7073},33,[688,7075,3953],{"class":859},[688,7077,7079],{"class":690,"line":7078},34,[688,7080,7081],{"class":859}," * [同构] 生成 AES-GCM 256位加密密钥\n",[688,7083,7085],{"class":690,"line":7084},35,[688,7086,4010],{"class":859},[688,7088,7090,7092,7095,7098,7100,7103,7106,7109,7111,7113,7116,7118],{"class":690,"line":7089},36,[688,7091,777],{"class":776},[688,7093,7094],{"class":2260}," const",[688,7096,7097],{"class":787}," generateAESKey ",[688,7099,2266],{"class":791},[688,7101,7102],{"class":2260}," async",[688,7104,7105],{"class":791}," ():",[688,7107,7108],{"class":694}," Promise",[688,7110,935],{"class":791},[688,7112,3490],{"class":694},[688,7114,7115],{"class":791},">",[688,7117,4202],{"class":2260},[688,7119,871],{"class":791},[688,7121,7123,7125,7127,7129,7131,7133],{"class":690,"line":7122},37,[688,7124,4261],{"class":776},[688,7126,4105],{"class":776},[688,7128,6792],{"class":787},[688,7130,2304],{"class":791},[688,7132,4380],{"class":783},[688,7134,4383],{"class":797},[688,7136,7138,7140,7142,7144,7146,7148,7150,7152,7155,7157,7159],{"class":690,"line":7137},38,[688,7139,5371],{"class":791},[688,7141,5374],{"class":797},[688,7143,801],{"class":791},[688,7145,831],{"class":791},[688,7147,4400],{"class":697},[688,7149,807],{"class":791},[688,7151,4128],{"class":791},[688,7153,7154],{"class":797}," length",[688,7156,801],{"class":791},[688,7158,4414],{"class":2478},[688,7160,5385],{"class":791},[688,7162,7164,7166,7168],{"class":690,"line":7163},39,[688,7165,5216],{"class":4427},[688,7167,4128],{"class":791},[688,7169,7170],{"class":859}," \u002F\u002F 允许导出\n",[688,7172,7174,7176,7178,7180,7182,7184,7186,7188,7190],{"class":690,"line":7173},40,[688,7175,4438],{"class":797},[688,7177,807],{"class":791},[688,7179,4443],{"class":697},[688,7181,807],{"class":791},[688,7183,4128],{"class":791},[688,7185,831],{"class":791},[688,7187,4452],{"class":697},[688,7189,807],{"class":791},[688,7191,814],{"class":797},[688,7193,7195],{"class":690,"line":7194},41,[688,7196,7197],{"class":797},"    )\n",[688,7199,7201],{"class":690,"line":7200},42,[688,7202,4270],{"class":791},[688,7204,7206],{"class":690,"line":7205},43,[688,7207,4088],{"emptyLinePlaceholder":1003},[688,7209,7211],{"class":690,"line":7210},44,[688,7212,3953],{"class":859},[688,7214,7216],{"class":690,"line":7215},45,[688,7217,7218],{"class":859}," * [同构] 导出 CryptoKey 密钥为可传输的 Base64 字符串\n",[688,7220,7222,7224,7226,7228,7231],{"class":690,"line":7221},46,[688,7223,3963],{"class":859},[688,7225,3966],{"class":776},[688,7227,3970],{"class":3969},[688,7229,7230],{"class":3982}," cryptoKey",[688,7232,7233],{"class":859}," - 浏览器或服务端生成的 CryptoKey 实体\n",[688,7235,7237],{"class":690,"line":7236},47,[688,7238,4010],{"class":859},[688,7240,7242,7244,7246,7249,7251,7253,7255,7258,7260,7263,7265,7267,7269,7271,7273,7275],{"class":690,"line":7241},48,[688,7243,777],{"class":776},[688,7245,7094],{"class":2260},[688,7247,7248],{"class":787}," exportKeyToBase64 ",[688,7250,2266],{"class":791},[688,7252,7102],{"class":2260},[688,7254,6752],{"class":791},[688,7256,7257],{"class":3982},"cryptoKey",[688,7259,801],{"class":791},[688,7261,7262],{"class":694}," CryptoKey",[688,7264,6851],{"class":791},[688,7266,7108],{"class":694},[688,7268,935],{"class":791},[688,7270,3977],{"class":694},[688,7272,7115],{"class":791},[688,7274,4202],{"class":2260},[688,7276,871],{"class":791},[688,7278,7280,7282,7285,7287,7289,7291,7293,7295,7297,7299,7302,7304,7306,7308],{"class":690,"line":7279},49,[688,7281,4041],{"class":2260},[688,7283,7284],{"class":787}," exported",[688,7286,4047],{"class":791},[688,7288,4105],{"class":776},[688,7290,6792],{"class":787},[688,7292,2304],{"class":791},[688,7294,6181],{"class":783},[688,7296,788],{"class":797},[688,7298,807],{"class":791},[688,7300,7301],{"class":697},"raw",[688,7303,807],{"class":791},[688,7305,4128],{"class":791},[688,7307,7230],{"class":787},[688,7309,911],{"class":797},[688,7311,7313,7315,7318,7320,7323],{"class":690,"line":7312},50,[688,7314,4261],{"class":776},[688,7316,7317],{"class":783}," bufferToBase64",[688,7319,788],{"class":797},[688,7321,7322],{"class":787},"exported",[688,7324,911],{"class":797},[688,7326,7328],{"class":690,"line":7327},51,[688,7329,4270],{"class":791},[688,7331,7333],{"class":690,"line":7332},52,[688,7334,4088],{"emptyLinePlaceholder":1003},[688,7336,7338],{"class":690,"line":7337},53,[688,7339,3953],{"class":859},[688,7341,7343],{"class":690,"line":7342},54,[688,7344,7345],{"class":859}," * [同构] 从 Base64 字符串导入还原为 CryptoKey 密钥对象\n",[688,7347,7349,7351,7353,7355,7358],{"class":690,"line":7348},55,[688,7350,3963],{"class":859},[688,7352,3966],{"class":776},[688,7354,3970],{"class":3969},[688,7356,7357],{"class":3982}," base64Key",[688,7359,7360],{"class":859}," - Base64 格式的密钥字符串\n",[688,7362,7364],{"class":690,"line":7363},56,[688,7365,4010],{"class":859},[688,7367,7369,7371,7373,7376,7378,7380,7382,7385,7387,7389,7391,7393,7395,7397,7399,7401],{"class":690,"line":7368},57,[688,7370,777],{"class":776},[688,7372,7094],{"class":2260},[688,7374,7375],{"class":787}," importKeyFromBase64 ",[688,7377,2266],{"class":791},[688,7379,7102],{"class":2260},[688,7381,6752],{"class":791},[688,7383,7384],{"class":3982},"base64Key",[688,7386,801],{"class":791},[688,7388,6854],{"class":694},[688,7390,6851],{"class":791},[688,7392,7108],{"class":694},[688,7394,935],{"class":791},[688,7396,3490],{"class":694},[688,7398,7115],{"class":791},[688,7400,4202],{"class":2260},[688,7402,871],{"class":791},[688,7404,7406,7408,7411,7413,7416,7418,7420],{"class":690,"line":7405},58,[688,7407,4041],{"class":2260},[688,7409,7410],{"class":787}," rawKeyBuffer",[688,7412,4047],{"class":791},[688,7414,7415],{"class":783}," base64ToBuffer",[688,7417,788],{"class":797},[688,7419,7384],{"class":787},[688,7421,911],{"class":797},[688,7423,7425,7427,7429,7431,7433,7435,7437,7439,7441,7443,7445,7447,7449,7451,7453,7455,7457,7459,7461,7463,7466,7468,7471,7473,7475,7477,7479,7481,7483,7485],{"class":690,"line":7424},59,[688,7426,4261],{"class":776},[688,7428,4105],{"class":776},[688,7430,6792],{"class":787},[688,7432,2304],{"class":791},[688,7434,6296],{"class":783},[688,7436,788],{"class":797},[688,7438,807],{"class":791},[688,7440,7301],{"class":697},[688,7442,807],{"class":791},[688,7444,4128],{"class":791},[688,7446,7410],{"class":787},[688,7448,4128],{"class":791},[688,7450,3973],{"class":791},[688,7452,5374],{"class":797},[688,7454,801],{"class":791},[688,7456,831],{"class":791},[688,7458,4400],{"class":697},[688,7460,807],{"class":791},[688,7462,6333],{"class":791},[688,7464,7465],{"class":4427}," true",[688,7467,4128],{"class":791},[688,7469,7470],{"class":797}," [",[688,7472,807],{"class":791},[688,7474,4443],{"class":697},[688,7476,807],{"class":791},[688,7478,4128],{"class":791},[688,7480,831],{"class":791},[688,7482,4452],{"class":697},[688,7484,807],{"class":791},[688,7486,7487],{"class":797},"])\n",[688,7489,7491],{"class":690,"line":7490},60,[688,7492,4270],{"class":791},[688,7494,7496],{"class":690,"line":7495},61,[688,7497,4088],{"emptyLinePlaceholder":1003},[688,7499,7501],{"class":690,"line":7500},62,[688,7502,3953],{"class":859},[688,7504,7506],{"class":690,"line":7505},63,[688,7507,7508],{"class":859}," * [同构] 使用 AES-GCM 算法加密字符串文本\n",[688,7510,7512,7514,7516,7518,7520],{"class":690,"line":7511},64,[688,7513,3963],{"class":859},[688,7515,3966],{"class":776},[688,7517,3970],{"class":3969},[688,7519,3983],{"class":3982},[688,7521,7522],{"class":859}," - 待加密的明文文本\n",[688,7524,7526,7528,7530,7532,7534],{"class":690,"line":7525},65,[688,7527,3963],{"class":859},[688,7529,3966],{"class":776},[688,7531,3970],{"class":3969},[688,7533,4529],{"class":3982},[688,7535,7536],{"class":859}," - CryptoKey 密钥对象\n",[688,7538,7540,7542,7544,7546],{"class":690,"line":7539},66,[688,7541,3963],{"class":859},[688,7543,3966],{"class":776},[688,7545,3995],{"class":3969},[688,7547,7548],{"class":859}," 返回打包了 \"IV\" 和 \"密文\" 的统一 Base64 字符串\n",[688,7550,7552],{"class":690,"line":7551},67,[688,7553,4010],{"class":859},[688,7555,7557,7559,7561,7564,7566,7568,7570,7572,7574,7576,7578,7580,7582,7584,7586,7588,7590,7592,7594,7596],{"class":690,"line":7556},68,[688,7558,777],{"class":776},[688,7560,7094],{"class":2260},[688,7562,7563],{"class":787}," encryptText ",[688,7565,2266],{"class":791},[688,7567,7102],{"class":2260},[688,7569,6752],{"class":791},[688,7571,4026],{"class":3982},[688,7573,801],{"class":791},[688,7575,6854],{"class":694},[688,7577,4128],{"class":791},[688,7579,4529],{"class":3982},[688,7581,801],{"class":791},[688,7583,7262],{"class":694},[688,7585,6851],{"class":791},[688,7587,7108],{"class":694},[688,7589,935],{"class":791},[688,7591,3977],{"class":694},[688,7593,7115],{"class":791},[688,7595,4202],{"class":2260},[688,7597,871],{"class":791},[688,7599,7601,7603,7605,7607,7609,7611],{"class":690,"line":7600},69,[688,7602,4041],{"class":2260},[688,7604,4044],{"class":787},[688,7606,4047],{"class":791},[688,7608,4050],{"class":791},[688,7610,4053],{"class":783},[688,7612,7613],{"class":797},"()\n",[688,7615,7617,7619,7621,7623,7625,7627,7629,7631,7633,7635,7637,7639,7641,7643],{"class":690,"line":7616},70,[688,7618,4041],{"class":2260},[688,7620,4598],{"class":787},[688,7622,801],{"class":791},[688,7624,4168],{"class":694},[688,7626,935],{"class":791},[688,7628,4812],{"class":694},[688,7630,7115],{"class":791},[688,7632,4047],{"class":791},[688,7634,4044],{"class":787},[688,7636,2304],{"class":791},[688,7638,4075],{"class":783},[688,7640,788],{"class":797},[688,7642,4026],{"class":787},[688,7644,911],{"class":797},[688,7646,7648],{"class":690,"line":7647},71,[688,7649,7650],{"class":859},"    \u002F\u002F 生成 12 字节的密码学安全伪随机数作为 IV (AES-GCM 标准推荐)\n",[688,7652,7654,7656,7658,7660,7662,7664,7666,7668,7670,7672,7674,7676,7678,7680,7682,7684,7686],{"class":690,"line":7653},72,[688,7655,4041],{"class":2260},[688,7657,4635],{"class":787},[688,7659,801],{"class":791},[688,7661,4168],{"class":694},[688,7663,935],{"class":791},[688,7665,4812],{"class":694},[688,7667,7115],{"class":791},[688,7669,4047],{"class":791},[688,7671,6802],{"class":787},[688,7673,2304],{"class":791},[688,7675,4644],{"class":783},[688,7677,788],{"class":797},[688,7679,4165],{"class":791},[688,7681,4168],{"class":783},[688,7683,788],{"class":797},[688,7685,4655],{"class":2478},[688,7687,7049],{"class":797},[688,7689,7691],{"class":690,"line":7690},73,[688,7692,7693],{"class":859},"    \u002F\u002F 执行加密，返回密文 ArrayBuffer\n",[688,7695,7697,7699,7702,7704,7706,7708,7710,7712,7714,7716,7718,7721,7723,7725,7727,7729,7731,7733,7735,7737,7739,7741,7743,7745,7747],{"class":690,"line":7696},74,[688,7698,4041],{"class":2260},[688,7700,7701],{"class":787}," cipherBuffer",[688,7703,801],{"class":791},[688,7705,6848],{"class":694},[688,7707,4047],{"class":791},[688,7709,4105],{"class":776},[688,7711,6792],{"class":787},[688,7713,2304],{"class":791},[688,7715,4443],{"class":783},[688,7717,788],{"class":797},[688,7719,7720],{"class":791},"{",[688,7722,5374],{"class":797},[688,7724,801],{"class":791},[688,7726,831],{"class":791},[688,7728,4400],{"class":697},[688,7730,807],{"class":791},[688,7732,4128],{"class":791},[688,7734,4635],{"class":797},[688,7736,801],{"class":791},[688,7738,4635],{"class":787},[688,7740,6333],{"class":791},[688,7742,4529],{"class":787},[688,7744,4128],{"class":791},[688,7746,4598],{"class":787},[688,7748,911],{"class":797},[688,7750,7752],{"class":690,"line":7751},75,[688,7753,7754],{"class":859},"    \u002F\u002F 将 IV（12字节）和密文拼接，方便单字符串传输\n",[688,7756,7758,7760,7763,7765,7767,7769,7771,7774,7776,7779,7782,7784,7786,7789],{"class":690,"line":7757},76,[688,7759,4041],{"class":2260},[688,7761,7762],{"class":787}," combined",[688,7764,4047],{"class":791},[688,7766,4050],{"class":791},[688,7768,4168],{"class":783},[688,7770,788],{"class":797},[688,7772,7773],{"class":787},"iv",[688,7775,2304],{"class":791},[688,7777,7778],{"class":787},"length",[688,7780,7781],{"class":791}," +",[688,7783,7701],{"class":787},[688,7785,2304],{"class":791},[688,7787,7788],{"class":787},"byteLength",[688,7790,911],{"class":797},[688,7792,7794,7797,7799,7802,7804,7806,7808,7810],{"class":690,"line":7793},77,[688,7795,7796],{"class":787},"    combined",[688,7798,2304],{"class":791},[688,7800,7801],{"class":783},"set",[688,7803,788],{"class":797},[688,7805,7773],{"class":787},[688,7807,4128],{"class":791},[688,7809,5176],{"class":2478},[688,7811,911],{"class":797},[688,7813,7815,7817,7819,7821,7823,7825,7827,7829,7832,7834,7836,7838,7840,7842],{"class":690,"line":7814},78,[688,7816,7796],{"class":787},[688,7818,2304],{"class":791},[688,7820,7801],{"class":783},[688,7822,788],{"class":797},[688,7824,4165],{"class":791},[688,7826,4168],{"class":783},[688,7828,788],{"class":797},[688,7830,7831],{"class":787},"cipherBuffer",[688,7833,4029],{"class":797},[688,7835,4128],{"class":791},[688,7837,4635],{"class":787},[688,7839,2304],{"class":791},[688,7841,7778],{"class":787},[688,7843,911],{"class":797},[688,7845,7847,7849,7851,7853,7856,7858,7860],{"class":690,"line":7846},79,[688,7848,4261],{"class":776},[688,7850,7317],{"class":783},[688,7852,788],{"class":797},[688,7854,7855],{"class":787},"combined",[688,7857,2304],{"class":791},[688,7859,6843],{"class":787},[688,7861,911],{"class":797},[688,7863,7865],{"class":690,"line":7864},80,[688,7866,4270],{"class":791},[688,7868,7870],{"class":690,"line":7869},81,[688,7871,4088],{"emptyLinePlaceholder":1003},[688,7873,7875],{"class":690,"line":7874},82,[688,7876,3953],{"class":859},[688,7878,7880],{"class":690,"line":7879},83,[688,7881,7882],{"class":859}," * [同构] 使用 AES-GCM 算法解密文本\n",[688,7884,7886,7888,7890,7892,7895],{"class":690,"line":7885},84,[688,7887,3963],{"class":859},[688,7889,3966],{"class":776},[688,7891,3970],{"class":3969},[688,7893,7894],{"class":3982}," combinedBase64",[688,7896,7897],{"class":859}," - 由 encryptText 生成的统一加密 Base64 字符串（内部含 IV）\n",[688,7899,7901,7903,7905,7907,7909],{"class":690,"line":7900},85,[688,7902,3963],{"class":859},[688,7904,3966],{"class":776},[688,7906,3970],{"class":3969},[688,7908,4529],{"class":3982},[688,7910,7911],{"class":859}," - 对应的 CryptoKey 密钥对象\n",[688,7913,7915],{"class":690,"line":7914},86,[688,7916,4010],{"class":859},[688,7918,7920,7922,7924,7927,7929,7931,7933,7936,7938,7940,7942,7944,7946,7948,7950,7952,7954,7956,7958,7960],{"class":690,"line":7919},87,[688,7921,777],{"class":776},[688,7923,7094],{"class":2260},[688,7925,7926],{"class":787}," decryptText ",[688,7928,2266],{"class":791},[688,7930,7102],{"class":2260},[688,7932,6752],{"class":791},[688,7934,7935],{"class":3982},"combinedBase64",[688,7937,801],{"class":791},[688,7939,6854],{"class":694},[688,7941,4128],{"class":791},[688,7943,4529],{"class":3982},[688,7945,801],{"class":791},[688,7947,7262],{"class":694},[688,7949,6851],{"class":791},[688,7951,7108],{"class":694},[688,7953,935],{"class":791},[688,7955,3977],{"class":694},[688,7957,7115],{"class":791},[688,7959,4202],{"class":2260},[688,7961,871],{"class":791},[688,7963,7965,7967,7970,7972,7974,7976,7978],{"class":690,"line":7964},88,[688,7966,4041],{"class":2260},[688,7968,7969],{"class":787}," combinedBuffer",[688,7971,4047],{"class":791},[688,7973,7415],{"class":783},[688,7975,788],{"class":797},[688,7977,7935],{"class":787},[688,7979,911],{"class":797},[688,7981,7983,7985,7988,7990,7992,7994,7996,7999],{"class":690,"line":7982},89,[688,7984,4041],{"class":2260},[688,7986,7987],{"class":787}," combinedBytes",[688,7989,4047],{"class":791},[688,7991,4050],{"class":791},[688,7993,4168],{"class":783},[688,7995,788],{"class":797},[688,7997,7998],{"class":787},"combinedBuffer",[688,8000,911],{"class":797},[688,8002,8004],{"class":690,"line":8003},90,[688,8005,8006],{"class":859},"    \u002F\u002F 从头部切出前 12 字节作为 IV\n",[688,8008,8010,8012,8014,8016,8018,8020,8022,8024,8026,8028,8030,8033,8035,8037,8039,8042],{"class":690,"line":8009},91,[688,8011,4041],{"class":2260},[688,8013,4635],{"class":787},[688,8015,801],{"class":791},[688,8017,4168],{"class":694},[688,8019,935],{"class":791},[688,8021,4812],{"class":694},[688,8023,7115],{"class":791},[688,8025,4047],{"class":791},[688,8027,7987],{"class":787},[688,8029,2304],{"class":791},[688,8031,8032],{"class":783},"slice",[688,8034,788],{"class":797},[688,8036,2499],{"class":2478},[688,8038,4128],{"class":791},[688,8040,8041],{"class":2478}," 12",[688,8043,911],{"class":797},[688,8045,8047],{"class":690,"line":8046},92,[688,8048,8049],{"class":859},"    \u002F\u002F 剩下的部分作为真正的密文\n",[688,8051,8053,8055,8057,8059,8061,8063,8065,8067,8069,8071,8073,8075,8077,8079],{"class":690,"line":8052},93,[688,8054,4041],{"class":2260},[688,8056,4675],{"class":787},[688,8058,801],{"class":791},[688,8060,4168],{"class":694},[688,8062,935],{"class":791},[688,8064,4812],{"class":694},[688,8066,7115],{"class":791},[688,8068,4047],{"class":791},[688,8070,7987],{"class":787},[688,8072,2304],{"class":791},[688,8074,8032],{"class":783},[688,8076,788],{"class":797},[688,8078,4655],{"class":2478},[688,8080,911],{"class":797},[688,8082,8084],{"class":690,"line":8083},94,[688,8085,4910],{"class":859},[688,8087,8089,8091,8093,8095,8097,8099,8101,8103,8105,8107,8109,8111,8113,8115,8117,8119,8121,8123,8125,8127,8129,8131,8133,8135,8137,8139,8141],{"class":690,"line":8088},95,[688,8090,4041],{"class":2260},[688,8092,4917],{"class":787},[688,8094,801],{"class":791},[688,8096,6848],{"class":694},[688,8098,4047],{"class":791},[688,8100,4105],{"class":776},[688,8102,6792],{"class":787},[688,8104,2304],{"class":791},[688,8106,4452],{"class":783},[688,8108,788],{"class":797},[688,8110,7720],{"class":791},[688,8112,5374],{"class":797},[688,8114,801],{"class":791},[688,8116,831],{"class":791},[688,8118,4400],{"class":697},[688,8120,807],{"class":791},[688,8122,4128],{"class":791},[688,8124,4635],{"class":797},[688,8126,801],{"class":791},[688,8128,4635],{"class":787},[688,8130,6333],{"class":791},[688,8132,4529],{"class":787},[688,8134,4128],{"class":791},[688,8136,4675],{"class":787},[688,8138,2304],{"class":791},[688,8140,6843],{"class":787},[688,8142,911],{"class":797},[688,8144,8146,8148,8150,8152,8154,8156],{"class":690,"line":8145},96,[688,8147,4041],{"class":2260},[688,8149,4999],{"class":787},[688,8151,4047],{"class":791},[688,8153,4050],{"class":791},[688,8155,5006],{"class":783},[688,8157,7613],{"class":797},[688,8159,8161,8163,8165,8167,8169,8171,8173],{"class":690,"line":8160},97,[688,8162,4261],{"class":776},[688,8164,4999],{"class":787},[688,8166,2304],{"class":791},[688,8168,5021],{"class":783},[688,8170,788],{"class":797},[688,8172,5026],{"class":787},[688,8174,911],{"class":797},[688,8176,8178],{"class":690,"line":8177},98,[688,8179,4270],{"class":791},[13,8181,8182],{},"TS 测试脚本",[13,8184,8185],{},"如果你在 Vite\u002FNuxt\u002FNext.js 项目或者使用了 ts-node \u002F bun \u002F deno 的后端环境中，可以直接引入并运行这段强类型的测试代码：",[679,8187,8190],{"className":766,"code":8188,"filename":8189,"language":769,"meta":502,"style":502},"import { decryptText, encryptText, exportKeyToBase64, generateAESKey, importKeyFromBase64 } from 'crypto'\n;(async () => {\n    try {\n        const rawData: string = '前后端全平台无缝传输的敏感数据 1234-abcd-TS-VERSION'\n        console.log('🔒 原始待加解密明文:', rawData)\n\n        \u002F\u002F ==========================================\n        \u002F\u002F 阶段一：在 A 端（例如 Node.js 后端）生成密钥并加密\n        \u002F\u002F ==========================================\n        const originalKey: CryptoKey = await generateAESKey()\n\n        \u002F\u002F 导出密钥为 Base64 字符串\n        const transportableKeyStr: string = await exportKeyToBase64(originalKey)\n        console.log('🔑 [传输中] 密钥已安全序列化为 Base64:', transportableKeyStr)\n\n        \u002F\u002F 对数据进行加密\n        const secureCipherText: string = await encryptText(rawData, originalKey)\n        console.log('📦 [传输中] 密文已安全打包（含IV）为 Base64:', secureCipherText)\n\n        \u002F\u002F ==========================================\n        \u002F\u002F 阶段二：在 B 端（例如现代 Web 浏览器前端）收到数据解密\n        \u002F\u002F ==========================================\n        console.log('\\n--- 数据已通过网络无缝抵达另一端 ---')\n\n        \u002F\u002F 1. 恢复强类型的 CryptoKey 实体\n        const receivedKey: CryptoKey = await importKeyFromBase64(transportableKeyStr)\n\n        \u002F\u002F 2. 还原数据内容\n        const decryptedOutput: string = await decryptText(secureCipherText, receivedKey)\n        console.log('🔓 [还原成功] 解密后的最终明文结果:', decryptedOutput)\n    } catch (error) {\n        console.error('❌ 密码学同构解密链路失败，错误原因: ', error)\n    }\n})()\n\n","test-crypto.ts",[42,8191,8192,8232,8248,8255,8276,8301,8305,8310,8315,8319,8338,8342,8347,8371,8394,8398,8403,8431,8454,8458,8462,8467,8471,8493,8497,8502,8526,8530,8535,8563,8586,8604,8628,8632],{"__ignoreMap":502},[688,8193,8194,8197,8199,8201,8203,8205,8207,8210,8212,8214,8216,8219,8222,8225,8227,8229],{"class":690,"line":691},[688,8195,8196],{"class":776},"import",[688,8198,3973],{"class":791},[688,8200,4888],{"class":787},[688,8202,4128],{"class":791},[688,8204,4563],{"class":787},[688,8206,4128],{"class":791},[688,8208,8209],{"class":787}," exportKeyToBase64",[688,8211,4128],{"class":791},[688,8213,4359],{"class":787},[688,8215,4128],{"class":791},[688,8217,8218],{"class":787}," importKeyFromBase64",[688,8220,8221],{"class":791}," }",[688,8223,8224],{"class":776}," from",[688,8226,831],{"class":791},[688,8228,6520],{"class":697},[688,8230,8231],{"class":791},"'\n",[688,8233,8234,8237,8239,8241,8244,8246],{"class":690,"line":506},[688,8235,8236],{"class":791},";",[688,8238,788],{"class":787},[688,8240,4015],{"class":2260},[688,8242,8243],{"class":791}," ()",[688,8245,4202],{"class":2260},[688,8247,871],{"class":791},[688,8249,8250,8253],{"class":690,"line":535},[688,8251,8252],{"class":776},"    try",[688,8254,871],{"class":791},[688,8256,8257,8260,8263,8265,8267,8269,8271,8274],{"class":690,"line":609},[688,8258,8259],{"class":2260},"        const",[688,8261,8262],{"class":787}," rawData",[688,8264,801],{"class":791},[688,8266,6854],{"class":694},[688,8268,4047],{"class":791},[688,8270,831],{"class":791},[688,8272,8273],{"class":697},"前后端全平台无缝传输的敏感数据 1234-abcd-TS-VERSION",[688,8275,8231],{"class":791},[688,8277,8278,8281,8283,8286,8288,8290,8293,8295,8297,8299],{"class":690,"line":624},[688,8279,8280],{"class":787},"        console",[688,8282,2304],{"class":791},[688,8284,8285],{"class":783},"log",[688,8287,788],{"class":797},[688,8289,807],{"class":791},[688,8291,8292],{"class":697},"🔒 原始待加解密明文:",[688,8294,807],{"class":791},[688,8296,4128],{"class":791},[688,8298,8262],{"class":787},[688,8300,911],{"class":797},[688,8302,8303],{"class":690,"line":856},[688,8304,4088],{"emptyLinePlaceholder":1003},[688,8306,8307],{"class":690,"line":863},[688,8308,8309],{"class":859},"        \u002F\u002F ==========================================\n",[688,8311,8312],{"class":690,"line":874},[688,8313,8314],{"class":859},"        \u002F\u002F 阶段一：在 A 端（例如 Node.js 后端）生成密钥并加密\n",[688,8316,8317],{"class":690,"line":890},[688,8318,8309],{"class":859},[688,8320,8321,8323,8326,8328,8330,8332,8334,8336],{"class":690,"line":896},[688,8322,8259],{"class":2260},[688,8324,8325],{"class":787}," originalKey",[688,8327,801],{"class":791},[688,8329,7262],{"class":694},[688,8331,4047],{"class":791},[688,8333,4105],{"class":776},[688,8335,4359],{"class":783},[688,8337,7613],{"class":797},[688,8339,8340],{"class":690,"line":905},[688,8341,4088],{"emptyLinePlaceholder":1003},[688,8343,8344],{"class":690,"line":1334},[688,8345,8346],{"class":859},"        \u002F\u002F 导出密钥为 Base64 字符串\n",[688,8348,8349,8351,8354,8356,8358,8360,8362,8364,8366,8369],{"class":690,"line":1343},[688,8350,8259],{"class":2260},[688,8352,8353],{"class":787}," transportableKeyStr",[688,8355,801],{"class":791},[688,8357,6854],{"class":694},[688,8359,4047],{"class":791},[688,8361,4105],{"class":776},[688,8363,8209],{"class":783},[688,8365,788],{"class":797},[688,8367,8368],{"class":787},"originalKey",[688,8370,911],{"class":797},[688,8372,8373,8375,8377,8379,8381,8383,8386,8388,8390,8392],{"class":690,"line":1351},[688,8374,8280],{"class":787},[688,8376,2304],{"class":791},[688,8378,8285],{"class":783},[688,8380,788],{"class":797},[688,8382,807],{"class":791},[688,8384,8385],{"class":697},"🔑 [传输中] 密钥已安全序列化为 Base64:",[688,8387,807],{"class":791},[688,8389,4128],{"class":791},[688,8391,8353],{"class":787},[688,8393,911],{"class":797},[688,8395,8396],{"class":690,"line":1359},[688,8397,4088],{"emptyLinePlaceholder":1003},[688,8399,8400],{"class":690,"line":1367},[688,8401,8402],{"class":859},"        \u002F\u002F 对数据进行加密\n",[688,8404,8405,8407,8410,8412,8414,8416,8418,8420,8422,8425,8427,8429],{"class":690,"line":1375},[688,8406,8259],{"class":2260},[688,8408,8409],{"class":787}," secureCipherText",[688,8411,801],{"class":791},[688,8413,6854],{"class":694},[688,8415,4047],{"class":791},[688,8417,4105],{"class":776},[688,8419,4563],{"class":783},[688,8421,788],{"class":797},[688,8423,8424],{"class":787},"rawData",[688,8426,4128],{"class":791},[688,8428,8325],{"class":787},[688,8430,911],{"class":797},[688,8432,8433,8435,8437,8439,8441,8443,8446,8448,8450,8452],{"class":690,"line":1383},[688,8434,8280],{"class":787},[688,8436,2304],{"class":791},[688,8438,8285],{"class":783},[688,8440,788],{"class":797},[688,8442,807],{"class":791},[688,8444,8445],{"class":697},"📦 [传输中] 密文已安全打包（含IV）为 Base64:",[688,8447,807],{"class":791},[688,8449,4128],{"class":791},[688,8451,8409],{"class":787},[688,8453,911],{"class":797},[688,8455,8456],{"class":690,"line":1392},[688,8457,4088],{"emptyLinePlaceholder":1003},[688,8459,8460],{"class":690,"line":1400},[688,8461,8309],{"class":859},[688,8463,8464],{"class":690,"line":1408},[688,8465,8466],{"class":859},"        \u002F\u002F 阶段二：在 B 端（例如现代 Web 浏览器前端）收到数据解密\n",[688,8468,8469],{"class":690,"line":1416},[688,8470,8309],{"class":859},[688,8472,8473,8475,8477,8479,8481,8483,8486,8489,8491],{"class":690,"line":1425},[688,8474,8280],{"class":787},[688,8476,2304],{"class":791},[688,8478,8285],{"class":783},[688,8480,788],{"class":797},[688,8482,807],{"class":791},[688,8484,8485],{"class":787},"\\n",[688,8487,8488],{"class":697},"--- 数据已通过网络无缝抵达另一端 ---",[688,8490,807],{"class":791},[688,8492,911],{"class":797},[688,8494,8495],{"class":690,"line":1434},[688,8496,4088],{"emptyLinePlaceholder":1003},[688,8498,8499],{"class":690,"line":1443},[688,8500,8501],{"class":859},"        \u002F\u002F 1. 恢复强类型的 CryptoKey 实体\n",[688,8503,8504,8506,8509,8511,8513,8515,8517,8519,8521,8524],{"class":690,"line":1452},[688,8505,8259],{"class":2260},[688,8507,8508],{"class":787}," receivedKey",[688,8510,801],{"class":791},[688,8512,7262],{"class":694},[688,8514,4047],{"class":791},[688,8516,4105],{"class":776},[688,8518,8218],{"class":783},[688,8520,788],{"class":797},[688,8522,8523],{"class":787},"transportableKeyStr",[688,8525,911],{"class":797},[688,8527,8528],{"class":690,"line":1461},[688,8529,4088],{"emptyLinePlaceholder":1003},[688,8531,8532],{"class":690,"line":1470},[688,8533,8534],{"class":859},"        \u002F\u002F 2. 还原数据内容\n",[688,8536,8537,8539,8542,8544,8546,8548,8550,8552,8554,8557,8559,8561],{"class":690,"line":1479},[688,8538,8259],{"class":2260},[688,8540,8541],{"class":787}," decryptedOutput",[688,8543,801],{"class":791},[688,8545,6854],{"class":694},[688,8547,4047],{"class":791},[688,8549,4105],{"class":776},[688,8551,4888],{"class":783},[688,8553,788],{"class":797},[688,8555,8556],{"class":787},"secureCipherText",[688,8558,4128],{"class":791},[688,8560,8508],{"class":787},[688,8562,911],{"class":797},[688,8564,8565,8567,8569,8571,8573,8575,8578,8580,8582,8584],{"class":690,"line":1488},[688,8566,8280],{"class":787},[688,8568,2304],{"class":791},[688,8570,8285],{"class":783},[688,8572,788],{"class":797},[688,8574,807],{"class":791},[688,8576,8577],{"class":697},"🔓 [还原成功] 解密后的最终明文结果:",[688,8579,807],{"class":791},[688,8581,4128],{"class":791},[688,8583,8541],{"class":787},[688,8585,911],{"class":797},[688,8587,8588,8591,8594,8596,8599,8602],{"class":690,"line":7063},[688,8589,8590],{"class":791},"    }",[688,8592,8593],{"class":776}," catch",[688,8595,6752],{"class":797},[688,8597,8598],{"class":787},"error",[688,8600,8601],{"class":797},") ",[688,8603,792],{"class":791},[688,8605,8606,8608,8610,8612,8614,8616,8619,8621,8623,8626],{"class":690,"line":7068},[688,8607,8280],{"class":787},[688,8609,2304],{"class":791},[688,8611,8598],{"class":783},[688,8613,788],{"class":797},[688,8615,807],{"class":791},[688,8617,8618],{"class":697},"❌ 密码学同构解密链路失败，错误原因: ",[688,8620,807],{"class":791},[688,8622,4128],{"class":791},[688,8624,8625],{"class":787}," error",[688,8627,911],{"class":797},[688,8629,8630],{"class":690,"line":7073},[688,8631,893],{"class":791},[688,8633,8634,8636],{"class":690,"line":7078},[688,8635,908],{"class":791},[688,8637,8638],{"class":787},")()\n",[968,8640,8641],{},"html pre.shiki code .sHwdD, html code.shiki .sHwdD{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#546E7A;--shiki-default-font-style:italic;--shiki-dark:#676E95;--shiki-dark-font-style:italic}html pre.shiki code .s7zQu, html code.shiki .s7zQu{--shiki-light:#39ADB5;--shiki-light-font-style:italic;--shiki-default:#89DDFF;--shiki-default-font-style:italic;--shiki-dark:#89DDFF;--shiki-dark-font-style:italic}html pre.shiki code .s6hCs, html code.shiki .s6hCs{--shiki-light:#9C3EDA;--shiki-light-font-style:italic;--shiki-default:#C792EA;--shiki-default-font-style:italic;--shiki-dark:#C792EA;--shiki-dark-font-style:italic}html pre.shiki code .sFweD, html code.shiki .sFweD{--shiki-light:#E2931D;--shiki-light-font-style:italic;--shiki-default:#FFCB6B;--shiki-default-font-style:italic;--shiki-dark:#FFCB6B;--shiki-dark-font-style:italic}html pre.shiki code .sHdIc, html code.shiki .sHdIc{--shiki-light:#90A4AE;--shiki-light-font-style:italic;--shiki-default:#EEFFFF;--shiki-default-font-style:italic;--shiki-dark:#BABED8;--shiki-dark-font-style:italic}html pre.shiki code .spNyl, html code.shiki .spNyl{--shiki-light:#9C3EDA;--shiki-default:#C792EA;--shiki-dark:#C792EA}html pre.shiki code .s2Zo4, html code.shiki .s2Zo4{--shiki-light:#6182B8;--shiki-default:#82AAFF;--shiki-dark:#82AAFF}html pre.shiki code .sMK4o, html code.shiki .sMK4o{--shiki-light:#39ADB5;--shiki-default:#89DDFF;--shiki-dark:#89DDFF}html pre.shiki code .sTEyZ, html code.shiki .sTEyZ{--shiki-light:#90A4AE;--shiki-default:#EEFFFF;--shiki-dark:#BABED8}html pre.shiki code .swJcz, html code.shiki .swJcz{--shiki-light:#E53935;--shiki-default:#F07178;--shiki-dark:#F07178}html pre.shiki code .sfazB, html code.shiki .sfazB{--shiki-light:#91B859;--shiki-default:#C3E88D;--shiki-dark:#C3E88D}html pre.shiki code .sbssI, html code.shiki .sbssI{--shiki-light:#F76D47;--shiki-default:#F78C6C;--shiki-dark:#F78C6C}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sfNiH, html code.shiki .sfNiH{--shiki-light:#FF5370;--shiki-default:#FF9CAC;--shiki-dark:#FF9CAC}html pre.shiki code .sBMFI, html code.shiki .sBMFI{--shiki-light:#E2931D;--shiki-default:#FFCB6B;--shiki-dark:#FFCB6B}",{"title":502,"searchDepth":535,"depth":535,"links":8643},[8644,8645,8646,8647,8655,8656,8657,8663,8664,8665],{"id":3043,"depth":506,"text":3044},{"id":3067,"depth":506,"text":3068},{"id":3180,"depth":506,"text":3181},{"id":3201,"depth":506,"text":3202,"children":8648},[8649,8650,8651,8652,8653,8654],{"id":3208,"depth":535,"text":3209},{"id":3248,"depth":535,"text":3249},{"id":3311,"depth":535,"text":3312},{"id":3360,"depth":535,"text":3361},{"id":3393,"depth":535,"text":3394},{"id":3432,"depth":535,"text":3433},{"id":3515,"depth":506,"text":3516},{"id":3866,"depth":506,"text":3867},{"id":3919,"depth":506,"text":3920,"children":8658},[8659,8660,8661,8662],{"id":3926,"depth":535,"text":3927},{"id":5042,"depth":535,"text":5043},{"id":5554,"depth":535,"text":5555},{"id":6079,"depth":535,"text":6080},{"id":6381,"depth":506,"text":6382},{"id":6478,"depth":506,"text":6479},{"id":6688,"depth":506,"text":6689},{"name":2024,"symbol":2435,"icon":3011,"id":896},"2026-08-25 15:59:25",{"type":10,"value":8669},[8670,8673],[13,8671,8672],{},"在过去很长一段时间里，只要一提到在前端进行加密、解密、生成哈希（MD5\u002FSHA）或生成随机数，绝大多数开发者的第一反应就是 npm install crypto-js。作为曾经的功勋库，crypto-js 陪伴我们度过了无数个项目。",[13,8674,8675],{},"然而，随着 Web 标准的演进，现代浏览器早已内置了一个更强大、更安全、性能也更恐怖的官方武器——Web Crypto API。时至今日，如果你还在新项目里打包第三方的加密库，是时候了解一下为什么要换成原生 API 了。",{},"\u002Fblog\u002Fweb-crypto-api","---\ntitle: Web Crypto API 浏览器原生加密的终极指南\ndescription: Web Crypto API 是浏览器原生提供的一套加密接口，允许 JavaScript 脚本使用密码学原语来构建基于加密的系统。它自 2015 年 7 月起已得到各大浏览器的广泛支持，是一个相当成熟且稳定的 Web 标准。\narticleId: web-crypto-api\ncreateDate: \"2026-08-25 15:59:25\"\nupdateDate: \"2026-08-26 10:46:16\"\narticleThumb: https:\u002F\u002Fcdn.anyfork.top\u002Fimages\u002F20260825111119031.webp\ntagIds: \n    - 12\ncategoryId: 10\nisTop: false\nisRecommend: false\nisHot: true\nisHidden: false\nnavigation: true\n---\n在过去很长一段时间里，只要一提到在前端进行加密、解密、生成哈希（MD5\u002FSHA）或生成随机数，绝大多数开发者的第一反应就是 npm install crypto-js。作为曾经的功勋库，crypto-js 陪伴我们度过了无数个项目。\n\n然而，随着 Web 标准的演进，现代浏览器早已内置了一个更强大、更安全、性能也更恐怖的官方武器——Web Crypto API。时至今日，如果你还在新项目里打包第三方的加密库，是时候了解一下为什么要换成原生 API 了。\n\u003C!--more-->\n\n## 1. 什么是 Web Crypto API？\nWeb Crypto API 是浏览器原生提供的一套加密接口，允许 JavaScript 脚本使用密码学原语来构建基于加密的系统。它自 2015 年 7 月起已得到各大浏览器的广泛支持，是一个相当成熟且稳定的 Web 标准。\n\n在 Web Crypto 规范诞生前，部分浏览器为了满足加密需求，在全局实现了一个名为 Crypto 的接口(即 window.crypto)。但正由于这样，当时各大浏览器的实现缺乏统一的 W3C 标准，定义模糊，且在密码学意义上是不够严谨和安全的（not cryptographically sound）。\n\n当正式的 Web Crypto API 标准（支持 encrypt、decrypt、sign、verify 等现代异步加密操作）推出时，为了彻底避免和老旧、不安全的旧接口方法发生命名混淆与冲突，W3C 规范决定将这些低底层的密码学核心方法群全部打包进一个全新的独立接口中，这就是 SubtleCrypto。你可以通过全局对象的 crypto.subtle 属性来访问它。\n\n由于旧的全局 crypto 对象并没有被废除（它依然保留了诸如密码学安全的伪随机数生成器 crypto.getRandomValues() 和 crypto.randomUUID() 等简单的同步方法），因此所有新版的 SubtleCrypto 复杂加密算法全部被挂载到了它的下级属性中。你必须通过 crypto.subtle 属性才能访问到这一整套现代加解密方法。\n\nNode.js 最早在 v15.0.0 引入了 Web Crypto API 接口。直到 Node.js 19.0.0，官方正式去掉了实验性警告，将其标记为 稳定版本（Stable），并且默认在全局作用域挂载了 globalThis.crypto。\n\n::warning\n重要提示：在现代浏览器中，为了保障密钥安全，crypto.subtle 属性被限制为只能在安全上下文（HTTPS 协议或 localhost）下才能被成功访问。如果你在普通的非加密 HTTP 网页里调用 crypto.subtle，它会直接返回 undefined。\n::\n## 2. 为什么选择 Web Crypto API？\n在决定是否在 Web 项目中使用 Web Crypto API 时，通常是因为它相比传统的第三方加密库（如 CryptoJS, jsencrypt, forge 等）在安全性、性能和原生支持度上带来了降维打击的优势。\n1. **防 XSS 攻击的“密钥黑盒”机制（核心安全优势）** : 这是选择Web Crypto API最关键的安全理由。\n- **传统加密库的痛点：** 如果你用 CryptoJS，密钥必须以明文字符串或 Uint8Array 的形式存在于 JavaScript 变量、localStorage 或 sessionStorage 中。一旦网站遭受 XSS（跨站脚本）攻击，黑客可以直接读取并盗走你的密钥。\n- **Web Crypto 的解决办法：** 它引入了 CryptoKey 对象。在生成或导入密钥时，如果设置 extractable: false，这个密钥在创建后将永远无法通过 JavaScript 读取、打印或导出。它被死死封锁在浏览器的底层安全内存中。黑客即使通过 XSS 控制了你的页面，也拿不到密钥本身，只能在本地调用它，大大降低了密钥泄露的风险。\n2. **硬件级的高性能与非阻塞（异步原生）**\n- **原生 C++ \u002F 汇编实现：** Web Crypto API 是由浏览器底层（基于 OpenSSL 或操作系统自带的密码学库）用原生代码实现的。相比纯 JavaScript 编写的加密库，它的运行速度快了数十倍到上百倍。\n- **异步非阻塞：** 传统的加密库（如 CryptoJS）大都是同步执行的。当计算大文件哈希，或者进行复杂的 RSA 大素数生成时，会直接卡死浏览器的 UI 主线程，导致页面瞬间卡顿或未响应。而 Web Crypto 的所有核心方法（如 encrypt、digest）全部是异步 Promise 机制，浏览器会将其交给底层的系统线程去算，完全不影响前端界面的流畅度。\n3. **密码学安全的伪随机数生成器 (CSPRNG)**: 在密码学中，随机数的质量决定了加密的生死。\n- **传统的隐患：** JavaScript 原生的 Math.random() 是非密码学安全的，它的随机轨迹可以被算法预测，绝不能用于生成加密密钥、IV（初始化向量）或盐值。\n- **Web Crypto 的保障：** 提供了 crypto.getRandomValues()，它直接调用操作系统底层的硬件熵源（如系统的 \u002Fdev\u002Furandom 或 CAPI），生成密码学安全的高强度真随机数，保障加密系统的根基不可被破解。\n4. **零依赖，极致减小打包体积 (Zero-Bundle)**\n- **传统加密库的痛点：** 引入 CryptoJS、JSEncrypt 或 Forge 会让前端的 Vendor 打包体积（Bundle Size）平白无故增加几十 KB 甚至几百 KB，影响首屏加载速度。\n- **Web Crypto 的优势：** 它是现代浏览器的标准内置 API，不需要 npm install 任何第三方包。直接开箱即用，让前端工程更加轻量、纯净。\n5. **行业标准规范，跨端兼容性完美**\n- **大厂和 W3C 背书：** 它遵循严格的 W3C 规范，由各大浏览器厂商共同维护和不断优化。\n- **环境通用性：** 目前 Web Crypto API 的覆盖率已接近 100%。不仅在 Chrome、Safari、Edge、Firefox 浏览器中完美支持，在现代的 Node.js (v15+)、Deno、Bun 以及 Cloudflare Workers 等服务端\u002F边缘计算环境中也实现了完全一致的 API。这使得你写的一套加解密代码，可以无缝在前端和服务端之间复用。\n## 3. 什么时候不应该选择它？\n使用 Web Crypto API 有唯一的限制条件：\n- **非安全上下文限制：** 它强制要求 HTTPS 环境（本地开发支持 localhost 或 127.0.0.1）。如果你的网站部署在纯 HTTP 生产环境下，crypto.subtle 会直接返回 undefined 无法工作。\n- **需要高层封装：** 它提供的是非常底层的“密码学原语”（Raw Primitives），不提供像“一键生成 JWT”或者“一键做密码信封”这样的高级功能，开发者需要自己处理字节对齐、IV 传递等微秒的细节。\n## 4. Web Crypto API核心接口\nWeb Crypto API（通常称为 crypto.subtle）的核心接口主要由以下 5 个标准接口（Interfaces） 和 1 个全局入口 组成。它们构成了浏览器原生密码学生态的基石。\n### 4.1 全局入口对象Crypto\n虽然这不是单独的加解密接口，但它是所有 Web 加密功能的总入口，挂载在全局 window.crypto（或 globalThis.crypto）上。Crypto提供基础的加密功能，主要包括：\n- **核心作用：** 提供基础的同步安全原语。\n- **常用方法：**\n   - **crypto.getRandomValues()：** 生成密码学安全的伪随机数（常用于生成 IV 或盐）。\n   - **crypto.randomUUID()：** 快速生成一个符合 RFC 4122 标准的随机 UUID 字符串。\n   - **crypto.subtle：** 指向核心异步加密接口 SubtleCrypto 的指针。\n### 4.2  核心操作员SubtleCrypto\n这是整个 Web Crypto API 中最重要、最核心的接口。所有的实际异步加解密、签名、密钥派生等操作都在这里执行。SubtleCrypto 提供的主要方法包括：\n- **核心作用：** 提供一系列底层、底层的异步密码学原语方法（全部返回 Promise）。\n- **核心方法（按功能分类）：**\n    - **加密与解密：** encrypt() \u002F decrypt()\n    - **签名与验证：** sign() \u002F verify()\n    - **哈希计算：** digest()\n    - **密钥生成：** generateKey()\n    - **密钥导入与导出：** importKey() \u002F exportKey()\n    - **密钥包装与解包：** wrapKey() \u002F unwrapKey()\n    - **密钥派生：** deriveKey() \u002F deriveBits()\n### 4.3 密钥实体CryptoKey\n该接口代表一个在浏览器中生成的、受保护的密钥对象。为了安全，浏览器的 SubtleCrypto 方法不直接操作纯文本的密钥字符串（如 \"my-secret-key\"），而是操作 CryptoKey 实例。\n- **核心作用：** 作为参数传递给 SubtleCrypto 的各项方法。\n- **核心属性（只读）：**\n   - **type：** 密钥类型（\"secret\" 对称加密、\"private\" 私钥、\"public\" 公钥）。\n   - **extractable：** 布尔值。如果为 false，该密钥在创建后绝对无法从内存中被导出或用 JS 打印出来，防止 XSS 攻击窃取密钥。\n   - **algorithm：** 绑定该密钥的算法对象（例如绑定的 AES-GCM 或 RSA-PSS 等参数）。\n   - **usages：** 该密钥被允许的用途数组（例如 ['encrypt', 'decrypt']）。\n### 4.4 非对称密钥对CryptoKeyPair\n当使用 RSA、ECDSA 等非对称加密算法时，crypto.subtle.generateKey() 返回的不是单个密钥，而是一个包含公钥和私钥的 CryptoKeyPair 对象。\n- **核心作用：** 容器对象，封装非对称密钥对。\n- **核心属性：**\n   - **publicKey：** 一个 CryptoKey 实例，代表公钥（用于加密或验证签名）。\n   - **privateKey：** 一个 CryptoKey 实例，代表私钥（用于解密或生成签名）。\n### 4.5 算法配置对象AlgorithmIdentifier\n虽然它在技术上是一个规范定义的字典对象（Dictionary），而不是传统的类或接口，但它是每个 API 都必传的灵魂参数。\n- **核心作用：** 用来指定具体的密码学算法名称以及其特有的初始化附加参数（如 IV、盐值、计数器等）。\n- **常见表现形式：** \n   - **哈希配置：** { name: \"SHA-256\" }\n   - **AES-GCM 配置：** { name: \"AES-GCM\", iv: Uint8Array, tagLength: 128 }\n   - **PBKDF2（密码派生）配置：** { name: \"PBKDF2\", salt: Uint8Array, iterations: 100000, hash: \"SHA-256\" }\n\n### 4.6 核心接口对比速查表\n| 接口名称 | 角色定位 | 非安全上下文（HTTP）使用规则 | 典型返回值 \u002F 用途 |\n| --- | --- | --- | --- |\n| Crypto | 全局大管家 | 是（部分方法受限） | 提供crypto.subtle入口、用于生成UUID |\n| SubtleCrypto | 核心工具库 | 否（必须在HTTPS\u002Flocalhost环境下使用） | 返回`Promise\u003CArrayBuffer>`，用于生成密文、哈希等加密核心操作 |\n| CryptoKey | 密钥黑盒 | 否 | 作为安全载体，直接传递给加解密函数使用 |\n| CryptoKeyPair | 密钥对容器 | 否 | 封装返回包含publicKey和privateKey的密钥对对象 |\n## 5. Web Crypto API支持的主流算法\n1. **对称加密（Symmetric）表**\n\n| 算法标识符 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |\n| --- | --- | --- | --- |\n| AES-GCM（强烈推荐） | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 密钥支持128\u002F192\u002F256位，IV推荐96位（12字节） | 传输数据加解密、本地缓存加密，自带防篡改认证标签 |\n| AES-CBC | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 密钥支持128\u002F192\u002F256位，IV固定128位（16字节） | 兼容老旧遗留系统的对称加解密业务 |\n| AES-CTR | encrypt、decrypt、generateKey、importKey、exportKey、wrapKey、unwrapKey | 计数器模式，支持流式处理 | 高频非对齐的流式数据区块加解密 |\n\n- AES-GCM 是当前Web场景下的首选方案，硬件加速支持度高，同时自带消息认证能力，无需额外实现哈希校验逻辑，安全性和开发效率都更优。\n- 不同算法的IV长度要求严格，比如AES-CBC必须使用固定16字节的IV，否则会直接触发解密失败。\n- 三款算法都完整覆盖了密钥生成、导入导出、包裹\u002F解包裹密钥的全流程操作，完全适配Web Crypto的全场景开发需求。\n\n2. **非对称加密算法（Asymmetric）表**\n\n| 算法标识符 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |\n| --- | --- | --- | --- |\n| RSA-OAEP（强烈推荐） | encrypt, decrypt, generateKey, importKey, exportKey, wrapKey, unwrapKey | 模数：2048, 4096 位哈希：推荐 SHA-256 | 混合加密体系中用于加密“对称密钥”，或加密超短敏感字段 |\n\nRSA-OAEP是Web场景下非对称加密的首选方案，它通过最优非对称加密填充机制，解决了传统“教科书RSA”的密文可延展性安全缺陷，在随机预言模型下可以抵御适应性选择密文攻击。由于非对称加密的特性，它不适合直接加密大体积数据，行业通用的最佳实践就是用它加密AES等对称密钥，再用对称算法加密实际业务数据，兼顾安全性和性能效率。\n\n3. **数字签名算法（Signature）表**\n\n| 算法名称 | 支持的操作 | 推荐搭配与技术参数| 典型应用场景 |\n| --- | --- | --- | --- |\n| ECDSA（强烈推荐） | sign、verify、generateKey、importKey、exportKey | 椭圆曲线：P-256、P-384、P-521 | 现代高并发身份验签、JWT签名、区块链节点验签 |\n| RSASSA-PKCS1-v1_5 | sign、verify、generateKey、importKey、exportKey | 模数≥2048位，哈希推荐SHA-256 | 传统企业级API签名、老旧版本JWT签名验签 |\n| RSA-PSS | sign、verify、generateKey、importKey、exportKey | RSA概率签名方案，密码学严谨度极高 | 金融级或司法级等高安全要求的数字签名场景 |\n| HMAC | sign、verify、generateKey、importKey、exportKey | 基于哈希的密钥消息认证码 | 前后端API请求防篡改哈希签名，如AWS签名机制 |\n\n- ECDSA 是Web场景下的首选签名方案，密钥体积小、验签速度快，非常适配高并发的现代Web服务。\n- RSA系列算法兼容性极强，适合需要对接老旧系统的业务场景，其中RSA-PSS的抗攻击能力远高于传统的RSASSA-PKCS1-v1_5。\n- HMAC实现成本低，是轻量API防篡改签名的最优选择。\n\n4. **单向哈希算法（Digest）表**\n\n| 算法名称 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |\n| --- | --- | --- | --- |\n| SHA-256 | digest | 无需密钥，单向不可逆 | 文件完整性校验（秒传场景）、数据防篡改指纹生成 |\n| SHA-384 | digest | 无需密钥，单向不可逆，安全强度高于SHA-256 | 高敏感数据校验、金融级业务的完整性验证 |\n| SHA-512 | digest | 无需密钥，单向不可逆，安全强度最高 | 司法级、军工级等高安全要求场景的哈希校验 |\n\n这三款同属SHA-2家族哈希算法，都具备单向不可逆的核心特性，无法从最终哈希值反推原始输入内容，仅用于生成数据唯一指纹，不能直接用于加解密还原原始数据。安全强度随输出位数提升而升高，同时计算性能也会小幅下降，普通业务场景优先选择SHA-256即可满足需求。\n\n5. **密钥派生算法（Key Derivation）表**\n\n| 算法名称 | 支持的操作 | 推荐搭配与技术参数 | 典型应用场景 |\n| --- | --- | --- | --- |\n| ECDH | deriveKey、deriveBits、generateKey、importKey、exportKey | 椭圆曲线：Diffie-Hellman | 两个终端在不安全信道中，安全地协商出一致的对称密钥 |\n| PBKDF2 | deriveKey、deriveBits、importKey | 基于密码的密钥派生，推荐迭代≥100000次 | 将用户输入的简短文明密码，转换为符合AES标准的高强度密钥 |\n| HKDF | deriveKey、deriveBits、importKey | 基于HMAC的密钥扩展算法 | 从一个基础主密钥，安全地扩展派生出多个子密钥 |\n\n- ECDH 是现代网络通信中密钥协商的核心方案，双方全程不需要直接传输最终的共享密钥，仅通过交换椭圆曲线公钥就能各自计算出完全一致的会话密钥，安全性极高。\n- PBKDF2 专门针对低熵的用户密码设计，通过高迭代次数大幅提升暴力破解的成本，是密码类密钥衍生的行业标准方案。\n- HKDF 面向已经具备高熵的主密钥材料，能高效地将单个主密钥拆分扩展为多个用途独立的子密钥，广泛应用于TLS等现代加密协议中。\n## 6. Web Crypto API 不支持的主流算法\n| 算法名称 | 不支持原因 | 替代实现方案 |\n| --- | --- | --- |\n| MD5 | 已被密码学界证实存在碰撞漏洞，安全性完全失效 | 引入crypto-js等第三方库实现或使用 SHA-256 |\n| SM2\u002FSM3\u002FSM4国密系列 | 浏览器国际标准未原生内置国密算法 | 引入sm-crypto等国密第三方库实现 |\n| ChaCha20-Poly1305\u002FEd25519 | 目前尚未完全普及到所有主流浏览器的 crypto.subtle 中 | 引入第三方加密库补充实现 |\n## 7. 常用操作示例\n以下是整理的 Web Crypto API (crypto.subtle) 最核心、最常用的四大标准代码示例（哈希生成、密钥生成、加密、解密）。需要特别提醒的是：所有 crypto.subtle 的操作都是异步的，都会返回一个 Promise，因此必须配合 async\u002Fawait 使用。\n### 7.1 对称加密AES-GCM示例\nAES-GCM (Galois\u002FCounter Mode) 是一种非常流行且强大的认证加密（AEAD, Authenticated Encryption with Associated Data） 模式。你可以把它理解为一种“二合一”的加密模式：它在高效加密数据的同时，还能确保数据没有被篡改。\n\nAES-GCM 是兼具高性能和高安全性的认证加密模式，特别适合在 Web Crypto API 这类原生环境中使用。它的安全性高度依赖于每次加密都使用唯一且不可预测的 IV。遵循这一核心原则，并妥善管理密钥、认真验证认证标签，就能为你的 Web 应用构建起一道坚实的数据安全防线。\n\n1. **计算文本哈希摘要 (Digest)**：这是最简单的操作，常用于生成文件的 SHA-256。它不需要密钥。\n```js\n\u002F**\n * 计算字符串的 SHA-256 哈希值\n * @param {string} text - 输入的文本\n * @returns {Promise\u003Cstring>} Hex 格式的哈希字符串\n *\u002F\nasync function generateHash(text) {\n    \u002F\u002F 1. 将文本编码为 Uint8Array 字节流\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    \u002F\u002F 2. 使用 crypto.subtle.digest 生成哈希（返回 ArrayBuffer）\n    const hashBuffer = await crypto.subtle.digest('SHA-256', data);\n\n    \u002F\u002F 3. 将 ArrayBuffer 转换为常见的十六进制 (Hex) 字符串\n    const hashArray = Array.from(new Uint8Array(hashBuffer));\n    const hashHex = hashArray.map(b => b.toString(16).padStart(2, '0')).join('');\n    \n    return hashHex;\n}\n\n\u002F\u002F 使用示例：\nconst sha256Result = await generateHash(\"Hello World\");\n```\n2. **生成对称加密密钥 (Generate Key)**：在加密数据之前，必须先通过 subtle 算法生成一个标准的加密密钥对象（CryptoKey）。这里以主流的 AES-GCM 算法为例：\n```js\n\u002F**\n * 生成一个用于 AES-GCM 加密的 256 位随机密钥\n * @returns {Promise\u003CCryptoKey>} 返回 CryptoKey 对象\n *\u002F\nasync function generateAESKey() {\n    return await crypto.subtle.generateKey(\n        {\n        name: 'AES-GCM',\n        length: 256 \u002F\u002F 密钥长度支持 128, 192, 或 256 位\n        },\n        false, \u002F\u002F 是否允许该密钥被导出（如果设为 false，密钥无法脱离当前浏览器，安全性极高）\n        ['encrypt', 'decrypt'] \u002F\u002F 该密钥被允许用来做什么操作\n    );\n}\n\n```\n3. **数据加密 Encrypt**：使用生成的密钥和 AES-GCM 算法对敏感文本进行加密。为了安全，AES-GCM 要求每次加密必须配合一个随机的初始化向量 (IV)。\n```js\n\u002F**\n * 对文本进行 AES-GCM 加密\n * @param {string} text - 待加密的明文\n * @param {CryptoKey} key - 上一步生成的 CryptoKey 对象\n * @returns {Promise\u003C{cipherText: ArrayBuffer, iv: Uint8Array}>} 返回密文和IV\n *\u002F\nasync function encryptText(text, key) {\n    const encoder = new TextEncoder();\n    const encodedText = encoder.encode(text);\n\n    \u002F\u002F AES-GCM 推荐使用 12位（96位）的高安全伪随机数作为 IV\n    \u002F\u002F 注意：这里使用的是外部同步方法 getRandomValues 生成随机数\n    const iv = crypto.getRandomValues(new Uint8Array(12));\n\n    \u002F\u002F 执行加密\n    const cipherText = await crypto.subtle.encrypt(\n        {\n        name: 'AES-GCM',\n        iv: iv \u002F\u002F 加密参数必须包含 IV\n        },\n        key, \u002F\u002F 密钥\n        encodedText \u002F\u002F 明文数据\n    );\n\n    \u002F\u002F 需要同时把密文和 IV 返回，因为解密时必须提供一模一样的 IV\n    return { cipherText, iv };\n}\n\n```\n4. **数据解密 Decrypt**：传入加密时使用的 CryptoKey、IV 和密文数组，将其还原为人类可读的明文字符串。\n```js\n\u002F**\n * 对 AES-GCM 密文进行解密\n * @param {ArrayBuffer} cipherText - 密文数据\n * @param {CryptoKey} key - 加密时使用的同一个密钥\n * @param {Uint8Array} iv - 加密时对应的随机 IV\n * @returns {Promise\u003Cstring>} 解密后的明文字符串\n *\u002F\nasync function decryptText(cipherText, key, iv) {\n    \u002F\u002F 执行解密\n    const decryptedBuffer = await crypto.subtle.decrypt(\n        {\n        name: 'AES-GCM',\n        iv: iv \u002F\u002F 必须传入与加密时完全相同的 IV\n        },\n        key,\n        cipherText\n    );\n\n    \u002F\u002F 将 ArrayBuffer 字节解码还原为字符串\n    const decoder = new TextDecoder();\n    return decoder.decode(decryptedBuffer);\n}\n```\n::tip\n如果要在项目中保存或传输生成的密钥和密文，通常需要通过 crypto.subtle.exportKey('raw', key) 将密钥导出成字节，再用 btoa() 或 Base64 方案 转换成可传输的字符串格式。\n::\n### 7.2 非对称加密RSA-OAEP示例\n非对称加密使用公钥加密、私钥解密，常用于非对称场景或加密对称密钥（如密钥交换）。这里使用最安全的 RSA-OAEP 算法。\n\n1. **生成 RSA 密钥对**\n```js\n\u002F**\n * 生成用于 RSA-OAEP 加解密的 2048 位密钥对\n * @returns {Promise\u003CCryptoKeyPair>} 返回包含公钥和私钥的对象\n *\u002F\nasync function generateRSAKeyPair() {\n    return await crypto.subtle.generateKey(\n        {\n            name: \"RSA-OAEP\",\n            modulusLength: 2048, \u002F\u002F 密钥长度，2048 位是目前的标准安全线\n            publicExponent: new Uint8Array([1, 0, 1]), \u002F\u002F 等于 65537，固定的密码学标准常数\n            hash: \"SHA-256\", \u002F\u002F 内部使用的哈希算法\n        },\n        true, \u002F\u002F 是否允许导出\n        [\"encrypt\", \"decrypt\"] \u002F\u002F 用途分配\n    );\n}\n```\n2. **使用公钥加密**\n```js\n\u002F**\n * 使用 RSA 公钥加密文本\n *\u002F\nasync function rsaEncrypt(text, publicKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    \u002F\u002F 返回 ArrayBuffer 格式的密文\n    return await crypto.subtle.encrypt(\n        { name: \"RSA-OAEP\" },\n        publicKey,\n        data\n    );\n}\n```\n3. **使用私钥解密**\n```js\n\u002F**\n * 使用 RSA 私钥解密文本\n *\u002F\nasync function rsaDecrypt(cipherText, privateKey) {\n    const decryptedBuffer = await crypto.subtle.decrypt(\n        { name: \"RSA-OAEP\" },\n        privateKey,\n        cipherText\n    );\n\n    const decoder = new TextDecoder();\n    return decoder.decode(decryptedBuffer);\n}\n```\n### 7.3  数字签名与验证 (RSASSA-PKCS1-v1_5)\n数字签名常用于防止数据被篡改并确认发送者身份。其逻辑是：私钥签名、公钥验证。这里使用最通用的 RSASSA-PKCS1-v1_5 算法。\n\n1. **生成数字签名的 RSA 密钥对**\n```js\n\u002F**\n * 生成用于数字签名的 RSA 密钥对\n *\u002F\nasync function generateSignKeyPair() {\n    return await crypto.subtle.generateKey(\n        {\n        name: \"RSASSA-PKCS1-v1_5\",\n        modulusLength: 2048,\n        publicExponent: new Uint8Array([1, 0, 1]),\n        hash: \"SHA-256\",\n        },\n        true,\n        [\"sign\", \"verify\"] \u002F\u002F 注意：这里的用途是签名和验证\n    );\n}\n```\n2. **使用私钥对数据生成签名**\n```js\n\n\u002F**\n * 使用私钥对数据生成签名\n * @returns {Promise\u003CArrayBuffer>} 返回签名摘要\n *\u002F\nasync function signData(text, privateKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    return await crypto.subtle.sign(\n        { name: \"RSASSA-PKCS1-v1_5\" },\n        privateKey,\n        data\n    );\n}\n```\n3. **公钥验证签名是否有效**\n```js\n\n\u002F**\n * 使用公钥验证签名是否有效\n * @returns {Promise\u003Cboolean>} 验证成功返回 true，被篡改则返回 false\n *\u002F\nasync function verifyData(text, signature, publicKey) {\n    const encoder = new TextEncoder();\n    const data = encoder.encode(text);\n\n    return await crypto.subtle.verify(\n        { name: \"RSASSA-PKCS1-v1_5\" },\n        publicKey,\n        signature, \u002F\u002F 之前生成的签名 ArrayBuffer\n        data \u002F\u002F 原始数据\n    );\n}\n```\n### 7.4 密钥的导入与导出 (Import \u002F Export)\n在实际业务中，密钥不能只存在于浏览器内存中，我们需要将其导出为字符串传给后端，或者将后端的字符串密钥导入为浏览器可识别的 CryptoKey 对象。\n\nWeb Crypto 支持多种格式，最常用的是 JWK (JSON Web Key) 格式（易读、跨平台）和 raw\u002Fspki\u002Fpkcs8（二进制字节流）。\n\n1. **导出密钥为 JSON (JWK 格式)**\n```js\n\u002F**\n * 将 CryptoKey 密钥对象导出为标准的 JSON 对象 (JWK)\n * @param {CryptoKey} key \n * @returns {Promise\u003CObject>} 可直接用于传输或 JSON.stringify 的对象\n *\u002F\nasync function exportKeyToJWK(key) {\n    return await crypto.subtle.exportKey(\"jwk\", key);\n}\n```\n2. **将 JSON 导入回密钥对象**\n```js\n\u002F**\n * 将 JWK 格式的 JSON 对象导入还原为可用的 AES-GCM 密钥\n * @param {Object} jwkObject \n * @returns {Promise\u003CCryptoKey>}\n *\u002F\nasync function importKeyFromJWK(jwkObject) {\n    return await crypto.subtle.importKey(\n        \"jwk\",\n        jwkObject,\n        { name: \"AES-GCM\" }, \u002F\u002F 必须指定对应的算法\n        true, \u002F\u002F 是否允许再次导出\n        [\"encrypt\", \"decrypt\"] \u002F\u002F 该密钥允许的用途\n    );\n}\n```\n## 8 避坑指南\n1. **陷阱一：IV 重复使用**\n- **问题：** 在 AES-GCM 等对称加密中，重复使用相同的初始化向量（IV）会导致密钥流重复，严重破坏加密安全性。\n- **正确做法：**\n    - 每次加密都使用 crypto.getRandomValues() 生成新的随机 IV\n    - AES-GCM 模式推荐使用 12 字节（非 16 字节！）的 IV\n    - 同一密钥下 IV 重复概率应低于 2⁻³²\n    - IV 不需要保密，但必须唯一——记得将 IV 与密文一起存储\n\n2. **陷阱二：密钥管理不当**\n- **常见错误：**\n    - 将密钥存储在 localStorage 或 Cookie 中\n    - 使用不安全的密钥传输方式\n    - 未设置适当的密钥使用限制\n- **安全建议：**\n    - 敏感会话密钥存储在 Web Worker 内存中\n    - 需要持久化的密钥使用 IndexedDB\n    - 使用 exportKey 和 importKey 安全地传输公钥\n\n3. **陷阱三：算法选择失误**\n- 不要使用 SHA-1 或 MD5 进行哈希\n- 不要使用 ECB 模式的 AES 加密\n- RSA 密钥长度至少 2048 位\n\n## 9 最佳实践\n1. **使用方式选型指南推荐**\n\n自 Node.js 19+ 起，Web Crypto API 已成为内置标准，可通过以下方式访问：\n```js\n\u002F\u002F 方式一：使用 globalThis\nconst { subtle } = globalThis.crypto;\n\n\u002F\u002F 方式二：使用 Node.js 模块\nconst { webcrypto } = require('node:crypto');\nconst { subtle } = webcrypto;\n```\n这两种方式虽然最终都能让你获取到 subtle 接口，但它们在设计哲学、模块机制、环境通用性以及底层依赖上有着本质的区别。\n\n| 对比维度 | 方式一：globalThis.crypto | 方式二：require('node:crypto') |\n| --- | --- | --- |\n| 本质定位 | Web标准API（与浏览器对齐） | Node.js原生模块API（特定服务端） |\n| 引入方式 | 零依赖，直接全局访问 | 需要显式通过require引入模块 |\n| 跨端通用性 | 极高（同构JavaScript） | 极低（只能在Node.js运行） |\n| 底层实现 | 浏览器内核（前端）\u002F Node V8底层封装（后端） | 强依赖Node.js内置的OpenSSL库 |\n| 执行上下文 | 必须在安全上下文（前端限制） | 服务端环境，无任何安全上下文限制 |\n\n**推荐使用\"globalThis.crypto\"的场景：** 正在编写纯前端项目、前后端全栈通用工具库（Isomorphic Lib），或者代码需要部署在 Cloudflare Workers、Vercel Edge 等现代边缘计算架构中。\n\n**推荐使用\"require('node:crypto')\"的场景：** 正在编写纯 Node.js 服务端项目（如大型后端服务、CLI 工具），且除了 Web Crypto 之外，还需要用到 Node.js 特有的高级安全特性（如流式加密大文件、硬件安全模块 HSM 交互等）。\n\n2. **使用细节建议**\n\nWeb Crypto API 是浏览器原生加密的黄金标准。它以零依赖、高性能、高安全性的优势，正在逐步取代各种第三方加密库。然而，正如其名 \"subtle\" 所警示的，加密是一个极其专业的领域，细微的错误就可能导致整个系统安全性完全失效。在使用 Web Crypto API 时：\n- 始终在 HTTPS 环境下使用\n- 选择正确的算法和参数（如 AES-GCM + 12 字节 IV）\n- 妥善管理密钥的生命周期\n\n## 10 加解密模板工具\n以下为您编写的这一套 AES-GCM 高级对称加密与解密工具函数。它做到了100% 零依赖、纯原生，通过 globalThis.crypto 实现完美同构。你不需要进行任何环境判断，同一段代码可以无缝运行在浏览器（安全上下文）、Node.js（19+）、Deno、Bun 以及边缘计算（如 Cloudflare Workers）中。\n\n为了解决数据传输与存储的问题，工具函数内部自动处理了 ArrayBuffer 与 Base64 字符串之间的相互转换。可以直接复制使用，代码如下：\n```ts [crypto.ts]\n\u002F**\n * 完美的同构 Web Crypto 安全加解密工具库 (TypeScript 箭头函数版本)\n * 适用环境：Browser (HTTPS), Node.js 19+, Deno, Bun, Cloudflare Workers\n *\u002F\n\n\u002F\u002F 1. 获取全局同构的 subtle 对象\nconst cryptoProvider = globalThis.crypto\nif (!cryptoProvider) {\n    throw new Error('当前运行环境不支持 Web Crypto API。请确保在 HTTPS、localhost 或现代 Node.js 19+ 环境下运行。')\n}\nconst subtle: SubtleCrypto = cryptoProvider.subtle\n\n\u002F**\n * 辅助函数：将 ArrayBuffer 转换为可传输的 Base64 字符串\n * 使用现代批处理写法apply，100% 消除严格模式下的 undefined 类型报错\n *\u002F\nconst bufferToBase64 = (buffer: ArrayBuffer): string => {\n    const bytes = new Uint8Array(buffer)\n    const binary = String.fromCharCode.apply(null, bytes as unknown as number[])\n    return btoa(binary)\n}\n\n\u002F**\n * 辅助函数：将 Base64 字符串还原为 ArrayBuffer\n * 使用 Array.from 映射，确保严格类型安全\n *\u002F\nconst base64ToBuffer = (base64: string): ArrayBuffer => {\n    const binaryString = atob(base64)\n    const bytes = Uint8Array.from(binaryString, (char) => char.charCodeAt(0))\n    return bytes.buffer\n}\n\n\u002F**\n * [同构] 生成 AES-GCM 256位加密密钥\n *\u002F\nexport const generateAESKey = async (): Promise\u003CCryptoKey> => {\n    return await subtle.generateKey(\n        { name: 'AES-GCM', length: 256 },\n        true, \u002F\u002F 允许导出\n        ['encrypt', 'decrypt']\n    )\n}\n\n\u002F**\n * [同构] 导出 CryptoKey 密钥为可传输的 Base64 字符串\n * @param cryptoKey - 浏览器或服务端生成的 CryptoKey 实体\n *\u002F\nexport const exportKeyToBase64 = async (cryptoKey: CryptoKey): Promise\u003Cstring> => {\n    const exported = await subtle.exportKey('raw', cryptoKey)\n    return bufferToBase64(exported)\n}\n\n\u002F**\n * [同构] 从 Base64 字符串导入还原为 CryptoKey 密钥对象\n * @param base64Key - Base64 格式的密钥字符串\n *\u002F\nexport const importKeyFromBase64 = async (base64Key: string): Promise\u003CCryptoKey> => {\n    const rawKeyBuffer = base64ToBuffer(base64Key)\n    return await subtle.importKey('raw', rawKeyBuffer, { name: 'AES-GCM' }, true, ['encrypt', 'decrypt'])\n}\n\n\u002F**\n * [同构] 使用 AES-GCM 算法加密字符串文本\n * @param text - 待加密的明文文本\n * @param key - CryptoKey 密钥对象\n * @returns 返回打包了 \"IV\" 和 \"密文\" 的统一 Base64 字符串\n *\u002F\nexport const encryptText = async (text: string, key: CryptoKey): Promise\u003Cstring> => {\n    const encoder = new TextEncoder()\n    const encodedText: Uint8Array\u003CArrayBuffer> = encoder.encode(text)\n    \u002F\u002F 生成 12 字节的密码学安全伪随机数作为 IV (AES-GCM 标准推荐)\n    const iv: Uint8Array\u003CArrayBuffer> = cryptoProvider.getRandomValues(new Uint8Array(12))\n    \u002F\u002F 执行加密，返回密文 ArrayBuffer\n    const cipherBuffer: ArrayBuffer = await subtle.encrypt({ name: 'AES-GCM', iv: iv }, key, encodedText)\n    \u002F\u002F 将 IV（12字节）和密文拼接，方便单字符串传输\n    const combined = new Uint8Array(iv.length + cipherBuffer.byteLength)\n    combined.set(iv, 0)\n    combined.set(new Uint8Array(cipherBuffer), iv.length)\n    return bufferToBase64(combined.buffer)\n}\n\n\u002F**\n * [同构] 使用 AES-GCM 算法解密文本\n * @param combinedBase64 - 由 encryptText 生成的统一加密 Base64 字符串（内部含 IV）\n * @param key - 对应的 CryptoKey 密钥对象\n *\u002F\nexport const decryptText = async (combinedBase64: string, key: CryptoKey): Promise\u003Cstring> => {\n    const combinedBuffer = base64ToBuffer(combinedBase64)\n    const combinedBytes = new Uint8Array(combinedBuffer)\n    \u002F\u002F 从头部切出前 12 字节作为 IV\n    const iv: Uint8Array\u003CArrayBuffer> = combinedBytes.slice(0, 12)\n    \u002F\u002F 剩下的部分作为真正的密文\n    const cipherText: Uint8Array\u003CArrayBuffer> = combinedBytes.slice(12)\n    \u002F\u002F 执行解密\n    const decryptedBuffer: ArrayBuffer = await subtle.decrypt({ name: 'AES-GCM', iv: iv }, key, cipherText.buffer)\n    const decoder = new TextDecoder()\n    return decoder.decode(decryptedBuffer)\n}\n```\nTS 测试脚本\n\n如果你在 Vite\u002FNuxt\u002FNext.js 项目或者使用了 ts-node \u002F bun \u002F deno 的后端环境中，可以直接引入并运行这段强类型的测试代码：\n\n```ts[test-crypto.ts]\nimport { decryptText, encryptText, exportKeyToBase64, generateAESKey, importKeyFromBase64 } from 'crypto'\n;(async () => {\n    try {\n        const rawData: string = '前后端全平台无缝传输的敏感数据 1234-abcd-TS-VERSION'\n        console.log('🔒 原始待加解密明文:', rawData)\n\n        \u002F\u002F ==========================================\n        \u002F\u002F 阶段一：在 A 端（例如 Node.js 后端）生成密钥并加密\n        \u002F\u002F ==========================================\n        const originalKey: CryptoKey = await generateAESKey()\n\n        \u002F\u002F 导出密钥为 Base64 字符串\n        const transportableKeyStr: string = await exportKeyToBase64(originalKey)\n        console.log('🔑 [传输中] 密钥已安全序列化为 Base64:', transportableKeyStr)\n\n        \u002F\u002F 对数据进行加密\n        const secureCipherText: string = await encryptText(rawData, originalKey)\n        console.log('📦 [传输中] 密文已安全打包（含IV）为 Base64:', secureCipherText)\n\n        \u002F\u002F ==========================================\n        \u002F\u002F 阶段二：在 B 端（例如现代 Web 浏览器前端）收到数据解密\n        \u002F\u002F ==========================================\n        console.log('\\n--- 数据已通过网络无缝抵达另一端 ---')\n\n        \u002F\u002F 1. 恢复强类型的 CryptoKey 实体\n        const receivedKey: CryptoKey = await importKeyFromBase64(transportableKeyStr)\n\n        \u002F\u002F 2. 还原数据内容\n        const decryptedOutput: string = await decryptText(secureCipherText, receivedKey)\n        console.log('🔓 [还原成功] 解密后的最终明文结果:', decryptedOutput)\n    } catch (error) {\n        console.error('❌ 密码学同构解密链路失败，错误原因: ', error)\n    }\n})()\n\n```",33.5,{"title":3036,"description":3047},{"loc":8677},"blog\u002F9.Web Crypto API浏览器原生加密的终极指南",[1334],[8685],{"name":8686,"symbol":8687,"icon":8688,"id":1334},"浏览器","web","mdi:web","2026-08-26 10:46:16",6031,"dFEAXKZPH39je0En-u1mE32EQFhpez4BM-ckLEv9l8w",1787994231063]