JWT 是什么?
JSON Web Token(基于 JSON 的 Web 令牌)
即:JWT 是后端给前端发的一个加密字符串,用来免登录、身份认证、鉴权,代替传统的 Session/Cookie。
就是一长串用点 . 分隔的字符串,长这样:
|
|
固定三段式,用 . 隔开。
RFC7519 标准:无规定最大长度,理论上可无限大。
JWT 只放必要、非敏感声明(一般建议<1KB),敏感数据可以使用 JWE 或放服务端。
| 结构 | 作用 |
|---|---|
| Header 头部 | 存加密算法、令牌类型,比如 HS256、RS256。 |
| Payload 载荷 | 存用户信息、业务数据(比如用户 ID、昵称、角色、过期时间)。 注意: 这里只是 Base64 编码,不是加密,别人能解码看到内容,不能放密码、敏感隐私数据。 |
| Signature 签名 | 用密钥加密的签名。作用: 防止被篡改,验证是不是我们后端发的合法令牌。 |
在线生成&验证网站:jwt.io
JWE 是什么?
JWE = JSON Web Encryption(RFC 7516)
它和 JWT 同属 JOSE(JSON Object Signing and Encryption)规范家族,专门解决一个问题:在令牌里安全传递敏感数据,不让中间人 / 前端直接看到内容。
结构:5 段(JWT 是 3 段)
JWT(JWS):Header.Payload.Signature(3 段)
JWE:ProtectedHeader.EncryptedKey.IV.Ciphertext.AuthenticationTag(5 段)
简单理解:
- JWT:透明信封 + 防篡改封条(内容可见、不可改)
- JWE:密封铁盒 + 锁(内容完全看不见,开锁才能读)
JOSE(总规范)
├─ JWS(签名,RFC7515)
├─ JWE(加密,RFC7516)
├─ JWT(令牌,RFC7519)← 基于 JWS/JWE
├─ JWK(密钥,RFC7517)
└─ JWA(算法,RFC7518)
鉴权 & 安全
常用鉴权流程
- 账号密码登录,后端校验通过。
- 后端生成一个 JWT 返回给前端。
- 前端把 JWT 存起来(LocalStorage / Cookie)。
- 之后每次请求接口,请求头带上 Authorization: Bearer JWT字符串。
- 后端解析、校验签名、看是否过期、拿到用户信息,放行 / 拒绝。
安全注意事项
常见安全风险(更多内容可参考:RFC8725: Threats and Vulnerabilities):
| 场景 | 说明 |
|---|---|
| 签名校验问题 | 部分jwt实现库在攻击者将 alg 改为 none 的时候,不校验签名。部分库在攻击者将 RS256 算法替换成 HS256 时,使用公钥作为对称密钥校验签名。 |
| 弱对称密钥 | 弱对称密钥可被离线爆破 |
| 未验证JWE中的JWS签名 | 部分库从JWE中解密出JWS后,没有对其中的签名进行校验 |
还有一种安全风险是,客户端使用jwt作为LICENSE,但使用了对称加密算法。这种情况下客户端中必定会存储一份加密密钥,存在被逆向提取风险(比如 Crawlab-pro)。
Golang 示例代码
- 使用
jwt.NewWithClaims方法创建 jwt 对象。 - 使用
NewWithClaims方法传入密钥进行签名。 - 使用
ParseWithClaims方法解析jwt字符串。 - 使用
token.valid验证jwt对象是否有效。
|
|
输出:
|
|
一些平台常见使用 双jwt 方案,即 AccessToken 和 RefreshToken。
- AccessToken 有业务操作权限,短有效期,在服务器没有存储状态,可以快速鉴权(只需要验证签名)。
- RefreshToken 没有业务操作权限,长有效期,只能用于续签 AccessToken,在服务器存储有状态,可以手动吊销。
RefreshToken可以不使用jwt形式,而使用比如uuid等字符串,存储到redis中,设置有效期,到期自动删除,或手动删除(吊销)。
前端使用AccessToken调接口,发现返回401,就自动使用RefreshToken去刷新,刷新失败就跳转登录。
如果采用单jwt在服务器存储状态,虽然也能实现手动吊销,但是当并发数量大时,数据库压力太大,会成为瓶颈甚至故障点。
本质上两个jwt没有区别,只是在签发时设置的有效期不同,同时在payload中标识了当前jwt可用于业务还是续签。比如:
- AccessToken 的 payload(业务令牌)
|
|
- RefreshToken 的 payload(仅续签令牌)
|
|