切换日光/暗黑模式
051. 密码工具、JWT 编解码与用户模型
学习目标
这一节实现认证系统的核心工具和用户模型。
学完后,你应该能理解:
- 为什么认证相关代码集中放到
auth模块; - 密码工具需要哪些方法;
- JWT token 为什么要区分编码和解码;
- token payload 里通常放什么;
- 用户模型为什么要区分私有模型和公开结构;
- 登录认证函数要检查哪些条件。
认证模块
用户认证属于后端核心能力。
可以在应用目录里创建独立的 auth 文件或模块,把认证相关工具集中放进去。
这样后续注册、登录、刷新 token、权限校验都能复用同一套逻辑。
密码工具
密码工具负责处理明文密码和密文密码。
它至少需要两个能力:
- 把明文密码哈希成密文;
- 验证明文密码和密文密码是否匹配。
工具初始化时会读取签名密钥和算法配置。
这样项目里其他地方不需要重复关心底层加密细节。
密码哈希测试
测试密码哈希时,不应该断言密文等于某个固定字符串。
因为同一个密码每次生成的密文可能不同。
更合理的测试是:
- 哈希后的值不等于明文;
- 正确密码可以通过验证;
- 错误密码不能通过验证。
这样测试的是行为,而不是某一次运行的随机结果。
JWT 编码
JWT 编码就是把字典对象转成 token 字符串。
payload 里可以放:
- 用户名;
- token 类型;
- 过期时间;
- 其他需要随 token 携带的轻量信息。
过期时间通常写入 exp 字段。
解码时,如果当前时间超过 exp,token 会被视为过期。
token 类型
同一个系统里会有多种 token。
例如:
access: 用来访问接口;refresh: 用来刷新登录状态;active: 用来激活账号。
把类型写进 payload 后,后端就能判断这个 token 应该用于哪个场景。
激活账号的 token 不应该拿来访问普通业务接口。
JWT 解码
JWT 解码会把 token 字符串还原成 payload。
解码失败可能有多种原因:
- token 已过期;
- token 被篡改;
- token 格式错误;
- 签名密钥不匹配;
- 算法不匹配。
工具函数可以统一处理这些异常。
无法解码时返回空值或抛出业务异常,再由上层决定如何响应。
并发生成 token
测试里还会验证并发生成 token 的情况。
同时生成多个 token 时,数量应该正确,结果也应该保持可用。
这类测试可以帮助你确认工具函数在多次调用下没有共享状态污染。
用户状态枚举
用户表里会有状态字段。
例如:
- 未激活;
- 正常;
- 禁用。
用枚举约束状态值,比到处写字符串更清晰。
它也能减少拼写错误带来的问题。
私有模型和公开结构
用户表里的字段不都能返回给前端。
例如哈希密码只能后端内部使用。
所以需要区分:
- 后端内部使用的用户模型;
- 可以返回给前端的公开用户结构;
- 注册时接收参数的结构。
公开结构里不应该包含哈希密码。
这是接口设计里的安全边界。
登录认证函数
登录认证函数接收用户名、密码和数据库 session。
它通常会做这些检查:
- 用户是否存在;
- 用户是否已激活;
- 用户是否被禁用;
- 密码是否正确。
任一步失败,都应该返回认证失败。
全部通过后,才返回公开用户信息。
本地跳过密码校验
项目里可能会有一个配置项,用来控制是否验证密码。
它适合本地初始化或忘记管理员密码时临时使用。
这个开关很危险。
线上环境不应该关闭密码校验。
这一节的重点
这一节把认证的底层积木搭起来。
你需要记住:
- 密码工具只暴露哈希和验证能力;
- JWT payload 要写清 token 类型和过期时间;
- 解码失败要统一处理;
- 用户公开结构不能包含敏感字段;
- 登录前必须检查用户状态和密码。