一、先记住区别
- BCrypt:保护用户密码,负责注册时保存密码哈希、登录时核对密码。
- JWT:登录成功后发给用户的身份令牌,后续访问接口时用于认证和权限检查。
- 哈希不等于加密:BCrypt 是单向密码哈希,不能解密还原密码。
- JWT 不等于加密 :本项目使用的是 HS256 签名 JWT,内容可被解码查看,但不能在不知道密钥的情况下有效篡改。
二、BCrypt:注册和登录
1. 注册流程
text
用户输入密码 123456
↓
BCrypt.encode("123456")
↓
自动生成随机盐 Salt,并进行密码哈希计算
↓
数据库保存 BCrypt 哈希字符串(包含算法版本、成本参数、盐、哈希结果)
为什么加盐? 即使两个人的密码相同,随机盐不同,生成的哈希通常也不同。
2. 登录流程
text
用户输入密码 123456
↓
从数据库读取已保存的 BCrypt 哈希
↓
BCrypt.matches(输入密码, 数据库哈希)
↓
取出哈希中记录的盐和成本参数,重新计算并比较
↓
相同 → 登录密码正确;不同 → 密码错误
重点 :matches() 不会解密数据库密码,也不会另生成一个随机盐;它使用旧哈希里存储的盐重新计算。
java
// 注册:保存哈希,而不是明文密码
String hash = passwordEncoder.encode("123456");
// 登录:验证用户输入的密码
boolean correct = passwordEncoder.matches("123456", hash);
其中 PasswordEncoder 是接口,BCryptPasswordEncoder 是实现类,passwordEncoder 只是变量名。
三、JWT:登录后生成令牌、访问时验证
1. 生成 JWT
text
用户登录,BCrypt 验证密码正确
↓
JwtEncoder 使用服务器 SecretKey 签名
↓
生成 JWT,返回给客户端
JWT 由三个部分组成:
text
Header.Payload.Signature
- Header(头部) :声明签名算法,例如
HS256。 - Payload(载荷) :保存用户 ID、角色
roles、过期时间exp等信息;可以被解码查看,不要放密码。 - Signature(签名):服务器使用SecretKey密钥,对前两部分计算出的防篡改校验值。
2. 访问接口
text
客户端发送请求
Authorization: Bearer <JWT>
↓
Spring Security 自动获取 Token
↓
JwtDecoder 用服务器密钥验证签名、检查有效期
↓
读取用户角色,检查接口权限
↓
通过 → 进入 Controller;失败 → 拒绝请求
- 401 Unauthorized:未提供有效身份凭证,例如没有 Token、Token 过期或签名错误。
- 403 Forbidden :身份已验证,但权限不够,例如
USER访问管理员接口。
3. 为什么修改角色会导致签名失效?
text
原来:Payload 里 roles = USER → 服务器生成签名 A
篡改:Payload 里 roles = ADMIN → 应该生成签名 B
签名取决于 Header + Payload + SecretKey。修改 Payload 后,旧签名 A 与服务器重新计算的签名不一致,就会被拒绝。攻击者没有服务器的密钥,无法生成正确的新签名。
四、常见专有名词
| 名词 | 通俗解释 |
|---|---|
| BCrypt | 专门保护密码的单向哈希算法,计算较慢,能增加暴力猜密码的成本。 |
| Hash(哈希) | 把数据计算成固定格式的摘要,通常不能反向还原原数据。 |
| Salt(盐) | 每次创建密码哈希时生成的随机值;不需要保密,防止相同密码总得到相同哈希。 |
| PasswordEncoder | Spring Security 提供的密码编码与校验接口。 |
| JWT | JSON Web Token,保存身份声明和签名的令牌格式。 |
| Token | 客户端携带的身份凭证。 |
| HS256 | JWT 中的算法名称,表示使用 HMAC-SHA256 计算签名。 |
| HMAC-SHA256 | 利用密钥 + 数据生成签名校验值的算法;没有正确密钥就难以伪造有效签名。 |
| SecretKey | 服务器保管的 JWT 签名密钥;与 BCrypt 的随机盐无关。 |
| JwtEncoder | 生成并签名 JWT;通常由登录业务代码主动调用。 |
| JwtDecoder | 解析并验证 JWT;Spring Security 可在请求认证时自动调用。 |
| Bearer | HTTP Authorization 请求头中携带 Token 的方式。 |
| Claim | JWT Payload 中的一项数据,例如 sub、roles、exp。 |
五、一句话串起完整流程
注册时 BCrypt 生成密码哈希并存库 → 登录时 BCrypt 使用旧盐验证密码 → 成功后 JwtEncoder 用 SecretKey 签发 JWT → 请求携带 Bearer Token → Spring Security 调用 JwtDecoder 验证 JWT → 按角色决定能否访问接口。
记忆口诀:BCrypt 管密码;JWT 管登录后的身份;Salt 用于密码哈希,SecretKey 用于 JWT 签名。