JWT安全最佳实践:Token认证风险与防护完整指南

了解JWT认证中的常见安全风险,掌握JWT密钥保护、Token过期、Refresh Token、Token存储、签名验证和权限控制等安全防护方法。

JWT安全最佳实践:Token认证原理、常见风险与防护指南

在现代Web应用开发中,用户登录、接口认证和权限控制是几乎所有系统都需要解决的问题。

传统Web应用经常使用Session保存用户登录状态,而在前后端分离、移动端应用、REST API以及微服务架构中,Token认证逐渐成为一种常见方案。

JWT(JSON Web Token)就是其中应用非常广泛的一种Token格式。

JWT可以将用户身份、Token有效期以及其他声明信息封装到一个Token中,客户端在访问需要认证的接口时携带这个Token,服务器验证Token后确定用户身份。

常见应用场景包括:

  • 用户登录认证
  • 前后端分离项目
  • REST API认证
  • 移动端App登录
  • 微服务身份认证
  • 单点登录
  • API访问授权
  • 服务之间的身份传递

不过,JWT本身并不等于“安全认证”。

如果没有正确处理JWT的签名算法、密钥、Token有效期、存储方式以及权限控制,就可能出现Token泄露、身份冒用、越权访问甚至整个认证系统被绕过等问题。

因此,理解JWT的工作原理以及正确的安全实践非常重要。

本文将系统介绍JWT是什么、JWT的组成结构、认证流程、常见算法、Access Token和Refresh Token、Token存储方式,以及生产环境中应该注意的安全问题。


一、什么是JWT?

JWT全称:

JSON Web Token

JWT是一种开放标准,用于在不同系统之间以JSON对象的形式传递声明信息。

JWT最大的特点是结构紧凑,并且可以通过数字签名验证Token是否被篡改。

一个JWT通常类似于:

xxxxx.yyyyy.zzzzz

也就是:

Header.Payload.Signature

三个部分使用英文句号.进行连接。

例如:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMDAwMSIsInJvbGUiOiJ1c2VyIn0
.
xxxxxxxxxxxxxxxxxxxxxxxx

其中:

  • Header:描述Token类型和签名算法
  • Payload:保存用户相关声明
  • Signature:验证Token完整性和真实性

二、JWT有什么作用?

JWT最常见的用途就是身份认证。

例如用户登录:

用户
 ↓
输入账号密码
 ↓
服务器验证账号密码
 ↓
验证成功
 ↓
生成JWT
 ↓
返回客户端

之后客户端访问:

GET /api/user/profile

可以携带:

Authorization: Bearer <JWT>

服务器收到请求以后:

获取JWT
 ↓
验证Token
 ↓
确认用户身份
 ↓
检查权限
 ↓
执行接口

这样服务器就可以知道:

这个请求是谁发起的。


三、JWT的主要特点

JWT之所以被广泛使用,主要有以下几个特点。

1. 结构简单

JWT本质上是一段字符串:

Header.Payload.Signature

非常适合通过HTTP请求传输。


2. 跨平台

JWT并不依赖某一种编程语言。

Java、JavaScript、Python、Go、PHP、C#等语言都可以生成和解析JWT。

例如:

Java服务
        ↓
生成JWT
        ↓
Vue前端
        ↓
携带JWT
        ↓
Node.js服务

不同技术栈之间也可以使用JWT进行身份认证。


3. 适合API认证

JWT经常用于:

前端
 ↓
API Gateway
 ↓
后端服务

尤其适合前后端分离项目。


4. 可以进行数字签名

JWT可以使用密钥生成Signature。

如果Token内容被修改,服务器重新计算签名时就会发现不匹配。

因此JWT可以用于验证:

  • Token完整性
  • Token来源
  • Token是否被篡改

四、JWT由哪几个部分组成?

JWT由三个部分组成:

Header.Payload.Signature

例如:

xxxxx
.
yyyyy
.
zzzzz

分别对应:

Header
Payload
Signature

下面分别介绍。


五、JWT Header是什么?

Header是JWT的第一部分。

常见Header:

{
  "alg": "HS256",
  "typ": "JWT"
}

其中:

alg

表示签名算法。

例如:

  • HS256
  • HS384
  • HS512
  • RS256
  • RS384
  • RS512
  • ES256

而:

typ

表示Token类型。

通常:

JWT

六、JWT Payload是什么?

Payload是JWT的第二部分。

它主要用于保存Claims,也就是声明信息。

例如:

{
  "sub": "10001",
  "username": "tom",
  "role": "user",
  "iat": 1720000000,
  "exp": 1720003600
}

这里包含:

sub
用户标识

username
用户名

role
角色

iat
Token签发时间

exp
Token过期时间

七、JWT常见Claims

JWT定义了一些常见的标准Claims。

Claim名称作用
issIssuerToken签发者
subSubjectToken主体
audAudienceToken接收方
expExpiration TimeToken过期时间
nbfNot BeforeToken生效时间
iatIssued AtToken签发时间
jtiJWT IDToken唯一标识

例如:

{
  "iss": "auth.example.com",
  "sub": "10001",
  "aud": "api-server",
  "iat": 1720000000,
  "exp": 1720003600
}

八、JWT Payload是不是加密的?

这是JWT使用过程中非常重要的一点。

普通JWT的Payload通常不是加密数据。

JWT主要是经过Base64URL编码,而不是简单意义上的加密。

因此拿到JWT以后,通常可以解析:

Header
Payload

例如:

{
  "sub": "10001",
  "role": "user"
}

就可能被读取。

因此不要把以下信息直接放入Payload:

  • 用户密码
  • 数据库密码
  • API密钥
  • 私钥
  • 银行卡信息
  • 身份证号码
  • 其他敏感信息

JWT签名可以防止数据被非法修改,但不等于隐藏数据内容。


九、JWT Signature是什么?

Signature是JWT第三部分。

它主要用于验证:

JWT是否被修改,以及是否由可信的一方签发。

以HS256为例,可以简单理解为:

HMACSHA256(
    Base64Url(Header)
    +
    "."
    +
    Base64Url(Payload),
    Secret
)

最终得到Signature。

完整过程:

Header
   +
Payload
   +
Secret
   ↓
签名算法
   ↓
Signature

最终组成:

Header.Payload.Signature

十、JWT为什么可以防止Token被篡改?

假设服务器签发:

{
  "sub": "10001",
  "role": "user"
}

攻击者修改成:

{
  "sub": "10001",
  "role": "admin"
}

由于Payload发生了变化:

原始Payload
      ↓
原始Signature

修改Payload
      ↓
重新计算Signature
      ↓
与原Signature不一致

服务器验证失败以后,就应该拒绝这个Token。

所以:

Signature是JWT安全体系中的核心机制。


十一、JWT完整认证流程

一个典型的JWT认证过程如下:

用户
 ↓
登录页面
 ↓
输入用户名和密码
 ↓
POST /login
 ↓
服务器验证账号密码
 ↓
验证成功
 ↓
生成Access Token
 ↓
生成Refresh Token
 ↓
返回客户端

客户端之后访问API:

客户端
 ↓
携带Access Token
 ↓
Authorization: Bearer JWT
 ↓
服务器
 ↓
验证JWT
 ↓
获取用户身份
 ↓
检查权限
 ↓
返回数据

当Access Token过期:

Access Token过期
 ↓
Refresh Token
 ↓
请求刷新接口
 ↓
验证Refresh Token
 ↓
生成新的Access Token
 ↓
继续访问API

十二、Authorization Bearer是什么?

JWT经常通过HTTP Authorization请求头传递。

例如:

GET /api/user/profile HTTP/1.1
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1Ni...

其中:

Bearer

表示客户端使用Bearer Token进行身份认证。

服务器通常需要从Authorization中提取:

Bearer
+
Token

然后验证Token。


十三、为什么生产环境必须使用HTTPS?

JWT本身是一种认证凭证。

如果攻击者能够在网络传输过程中获取Token,就可能冒充用户。

例如:

用户
 ↓
HTTP
 ↓
JWT
 ↓
服务器

如果网络环境不安全,Token可能被窃取。

因此生产环境应该:

用户
 ↓
HTTPS
 ↓
JWT
 ↓
服务器

HTTPS可以保护客户端和服务器之间的通信内容。

生产环境建议:

  • 全站使用HTTPS
  • HTTP自动跳转HTTPS
  • 开启安全TLS配置
  • 不通过HTTP发送JWT
  • 不在不安全网络中传输敏感认证信息

十四、JWT不能放在URL中

不推荐:

https://example.com/api/user?token=xxxxx

因为URL可能进入:

  • 浏览器历史记录
  • Web服务器日志
  • Nginx日志
  • CDN日志
  • 代理日志
  • 监控系统
  • Referer信息

因此更推荐:

Authorization: Bearer <JWT>

十五、Access Token是什么?

Access Token是访问API资源的凭证。

例如:

POST /api/order
Authorization: Bearer <Access Token>

服务器验证Access Token以后允许用户访问接口。

Access Token通常具有:

  • 较短生命周期
  • 明确的权限范围
  • 较小的Payload
  • 较低的泄露影响范围

十六、为什么Access Token应该设置较短有效期?

假设Access Token永久有效:

Token泄露
 ↓
攻击者获得Token
 ↓
长期使用
 ↓
用户身份长期被冒用

如果Token只有较短生命周期:

Token泄露
 ↓
攻击者获得Token
 ↓
Token到期
 ↓
无法继续使用

因此Access Token通常不建议设置成永久有效。

具体有效时间需要根据业务安全等级决定。


十七、Refresh Token是什么?

Refresh Token用于获取新的Access Token。

例如:

Access Token
有效期较短

Refresh Token
有效期较长

当Access Token过期:

客户端
 ↓
Refresh Token
 ↓
刷新接口
 ↓
认证服务器
 ↓
验证Refresh Token
 ↓
返回新的Access Token

这样就不需要让Access Token长期有效。


十八、Access Token和Refresh Token区别

项目Access TokenRefresh Token
用途访问API获取新Access Token
有效期较短较长
使用频率较低
泄露影响较短期风险更大
存储要求较高非常高
是否用于普通API

可以简单理解为:

Access Token
=
短期通行证

Refresh Token
=
长期续期凭证

十九、Refresh Token Rotation

对于安全要求较高的系统,可以使用Refresh Token Rotation。

流程:

旧Refresh Token
 ↓
刷新请求
 ↓
验证成功
 ↓
生成新的Access Token
+
新的Refresh Token
 ↓
旧Refresh Token失效

这样可以降低Refresh Token长期重复使用带来的风险。

如果系统发现一个已经失效的Refresh Token再次出现,还可以将其视为潜在的Token盗用信号。


二十、JWT最常见的安全问题

JWT常见安全风险包括:

安全问题可能造成的后果
Secret泄露Token可能被伪造
Private Key泄露攻击者可能签发Token
Token泄露用户身份被冒用
Token永久有效泄露后长期有效
不验证Signature可能接受伪造Token
不验证exp过期Token继续使用
Payload保存敏感数据敏感信息泄露
Token放URL容易进入日志
完整Token写日志凭证泄露
权限检查不足越权访问
XSSToken可能被窃取
CSRFCookie认证可能被滥用

二十一、JWT必须验证Signature

这是JWT开发中最重要的原则之一。

错误方式:

收到JWT
 ↓
解析Payload
 ↓
读取userId
 ↓
直接相信

攻击者可能修改:

{
  "userId": "10001",
  "role": "admin"
}

如果服务器没有验证Signature,就可能产生严重的权限绕过问题。

正确流程:

收到JWT
 ↓
验证Token格式
 ↓
验证Signature
 ↓
验证算法
 ↓
验证exp
 ↓
验证iss
 ↓
验证aud
 ↓
获取身份
 ↓
检查权限
 ↓
执行请求

二十二、不要相信JWT中的role

例如Token中存在:

{
  "sub": "10001",
  "role": "admin"
}

服务器不能简单地认为:

role = admin

就一定允许访问管理员接口。

应该:

验证JWT
 ↓
确定用户身份
 ↓
获取用户权限
 ↓
检查接口权限
 ↓
允许或者拒绝

对于重要权限,应该结合服务端权限数据进行判断。


二十三、JWT与权限控制不是一回事

JWT主要解决:

你是谁?

权限系统解决:

你可以做什么?

例如:

Authentication
身份认证
 ↓
用户是谁?

Authorization
权限控制
 ↓
用户可以访问什么?

因此:

JWT验证成功

不代表:

所有接口都可以访问

二十四、什么是水平越权?

假设:

用户A
userId = 10001

用户B
userId = 10002

用户A请求:

GET /api/user/10002

如果服务器仅仅验证:

JWT有效

就返回用户B的数据,就产生了水平越权。

正确方式应该进一步验证:

当前用户
+
目标资源
+
资源所属关系

只有满足业务权限以后才能访问。


二十五、什么是垂直越权?

例如:

普通用户

尝试访问:

/admin/users

服务器不能因为JWT有效就允许访问。

应该检查:

用户身份
+
用户角色
+
接口权限

如果权限不足:

403 Forbidden

二十六、JWT Secret为什么非常重要?

HS256等对称签名算法依赖Secret。

例如:

Secret
 ↓
签名JWT

服务器:

Secret
 ↓
验证JWT

如果Secret泄露,攻击者可能利用Secret生成合法Token。

因此Secret必须受到严格保护。


二十七、JWT Secret不要写死在代码里

不推荐:

const secret = "123456";

也不推荐:

jwt:
  secret: my-production-secret

然后直接提交到Git。

推荐:

环境变量
+
Secret管理系统
+
安全配置中心

例如:

JWT_SECRET

应用启动时读取。


二十八、不要把Secret提交到Git

错误流程:

Secret
 ↓
写入application.yml
 ↓
git add
 ↓
git commit
 ↓
git push

即使之后删除文件,Git历史中可能仍然存在。

如果生产Secret已经泄露:

立即生成新Secret
 ↓
停止使用旧Secret
 ↓
让旧Token失效
 ↓
重新签发Token

二十九、JWT应该使用什么算法?

常见算法:

算法类型特点
HS256HMAC对称密钥
HS384HMAC对称密钥
HS512HMAC对称密钥
RS256RSA非对称密钥
RS384RSA非对称密钥
RS512RSA非对称密钥
ES256ECDSA非对称密钥

三十、HS256和RS256区别

HS256

HS256使用同一个Secret:

Secret
 ↓
签名
 ↓
JWT
 ↓
Secret
 ↓
验证

优点:

  • 实现简单
  • 性能较好
  • 配置方便

适合:

  • 单体应用
  • 简单API
  • 服务数量较少的系统

缺点:

所有验证JWT的服务都需要知道Secret。


RS256

RS256使用:

Private Key
+
Public Key

认证服务:

Private Key
 ↓
签名JWT

业务服务:

Public Key
 ↓
验证JWT

这样业务服务不需要拥有签发Token的私钥。

比较适合:

  • 微服务
  • 统一认证中心
  • API Gateway
  • 单点登录
  • 大型系统

三十一、为什么微服务更适合非对称签名?

假设有:

认证服务
用户服务
订单服务
商品服务
支付服务

如果使用HS256:

认证服务
 ↓
Secret
 ↓
所有业务服务都需要Secret

一旦某个业务服务泄露Secret,风险可能扩散到整个系统。

使用RS256:

认证服务
 ↓
Private Key
 ↓
签发JWT

用户服务 → Public Key
订单服务 → Public Key
商品服务 → Public Key
支付服务 → Public Key

业务服务只能验证Token,而不能利用公钥签发新的合法Token。

因此对于统一认证中心和微服务架构,非对称签名通常更容易进行密钥隔离。


三十二、不要盲目信任alg

JWT Header中存在:

{
  "alg": "RS256"
}

服务器不应该完全根据客户端提供的alg决定验证方式。

应该在服务端明确配置:

允许的算法
 ↓
RS256

收到JWT:

Token alg
 ↓
是否符合服务端策略
 ↓
验证

这样可以避免因算法处理不当而产生安全漏洞。


三十三、JWT应该验证哪些Claims?

生产环境通常需要根据业务验证:

exp
iss
aud
nbf

其中最重要的包括:

exp

判断Token是否过期。

当前时间 > exp
 ↓
Token失效

iss

确认Token是不是由可信认证中心签发。

aud

确认Token是不是发给当前服务使用。

nbf

确认Token是否已经达到生效时间。


三十四、JWT Token存储在哪里?

前端常见的存储方式:

  • localStorage
  • sessionStorage
  • Cookie
  • 内存

不同方式有不同安全特点。

没有一种方式可以脱离整体架构直接判断为“绝对安全”。

需要结合:

  • XSS
  • CSRF
  • 跨域
  • 前端框架
  • API架构
  • Token生命周期

进行选择。


三十五、localStorage存JWT安全吗?

例如:

localStorage.setItem("token", jwt);

使用方便,但是存在一个重要风险:

如果网站发生XSS攻击,恶意JavaScript可能读取localStorage中的Token。

攻击过程可能类似:

XSS
 ↓
执行恶意JavaScript
 ↓
读取localStorage
 ↓
获取JWT
 ↓
冒充用户

因此使用localStorage保存JWT时,必须特别重视XSS防护。


三十六、HttpOnly Cookie是什么?

如果通过Cookie保存认证信息,可以设置:

Set-Cookie: token=xxx; HttpOnly; Secure; SameSite=Lax

其中:

HttpOnly

可以阻止普通JavaScript直接读取Cookie。

Secure

要求Cookie通过HTTPS发送。

SameSite

可以帮助降低部分跨站请求风险。

但是:

HttpOnly并不意味着网站完全安全。

如果使用Cookie进行身份认证,仍然需要正确设计CSRF防护。


三十七、JWT与XSS

XSS攻击可能让攻击者在用户浏览器中执行恶意JavaScript。

如果JWT可以被JavaScript直接读取:

XSS
 ↓
读取Token
 ↓
发送Token
 ↓
攻击者获得身份

因此应该:

  • 对用户输入进行正确处理
  • 避免不必要的HTML拼接
  • 对富文本进行过滤
  • 使用CSP
  • 控制第三方脚本
  • 及时更新依赖
  • 避免不必要的innerHTML

三十八、JWT与CSRF

如果JWT放在Cookie中,由于浏览器可能自动携带Cookie,就需要考虑CSRF。

攻击者可能诱导用户访问恶意网站:

恶意网站
 ↓
发送请求
 ↓
浏览器自动携带Cookie
 ↓
目标服务器

因此Cookie认证方案应该结合:

  • SameSite
  • CSRF Token
  • Origin检查
  • Referer检查
  • 请求来源控制

进行防护。


三十九、JWT注销为什么比较复杂?

Session模式下:

服务器
 ↓
Session

用户退出:

删除Session
 ↓
立即失效

JWT通常是:

JWT
 ↓
客户端持有
 ↓
服务器验证签名

如果服务器完全不保存Token状态,那么已经签发的Token可能在过期之前继续有效。

因此JWT注销需要额外设计。


四十、JWT如何实现退出登录?

常见方案包括:

方案一:短Token

让Access Token有效时间比较短。

Access Token
 ↓
较短有效期

用户退出后,Refresh Token失效。


方案二:Refresh Token撤销

服务器保存Refresh Token状态。

退出:

Refresh Token
 ↓
撤销

之后不能继续刷新Access Token。


方案三:Token黑名单

服务器保存已经撤销的Token或Token ID。

JWT
 ↓
jti
 ↓
Blacklist

验证时检查:

Token有效
+
Token不在Blacklist

方案四:Token版本号

用户数据保存:

tokenVersion = 1

JWT中:

tokenVersion = 1

强制退出所有设备后:

tokenVersion = 2

旧Token:

1 != 2

因此失效。


四十一、不要把完整JWT记录到日志

开发阶段经常会:

console.log(token);

生产环境不推荐。

因为日志可能进入:

  • Nginx
  • ELK
  • Loki
  • 云日志服务
  • 监控平台
  • 第三方日志系统

如果完整Token进入日志,攻击者获取日志以后可能直接获得用户身份凭证。

推荐:

不记录完整JWT

必要时只记录:

Token前几位
Token ID
用户ID
请求ID

并进行脱敏。


四十二、JWT Payload应该保持多大?

JWT会随着请求不断传输。

如果Payload保存大量数据:

用户信息
+
菜单
+
权限
+
订单
+
配置
+
其他业务数据

会导致JWT越来越大。

推荐只保存认证所需要的最小信息:

{
  "sub": "10001",
  "role": "user",
  "iat": 1720000000,
  "exp": 1720003600
}

详细业务数据应该通过API获取。


四十三、JWT不要保存密码

错误:

{
  "username": "tom",
  "password": "123456"
}

绝对不应该这样设计。

用户密码应该在服务器端使用安全的密码哈希算法进行处理。

常见选择:

  • Argon2
  • bcrypt
  • scrypt

JWT中只保存用户身份标识等必要信息。


四十四、JWT与密码哈希有什么区别?

JWT和密码哈希解决的是不同问题。

技术主要用途
JWT用户身份认证和Token传递
Hash数据摘要
bcrypt密码哈希
Argon2密码哈希
SHA-256通用哈希
HTTPS数据传输加密

例如用户登录:

用户名
+
密码
 ↓
服务器验证密码哈希
 ↓
登录成功
 ↓
生成JWT

所以:

JWT不是密码存储技术。


四十五、JWT与Session有什么区别?

对比JWTSession
服务端状态通常较少通常需要
TokenJWTSession ID
分布式部署比较方便通常需要共享Session
主动撤销需要额外设计相对简单
API场景常见也可以使用
微服务比较方便需要额外架构
Token大小相对较大通常较小

JWT并不一定比Session更安全。

真正重要的是:

认证方案是否正确设计和实现。


四十六、JWT与OAuth 2.0有什么区别?

JWT和OAuth 2.0经常一起出现,但它们不是同一个东西。

JWT:

一种Token格式。

OAuth 2.0:

一套授权框架。

可以理解为:

JWT
=
Token长什么样

OAuth 2.0
=
Token如何用于授权流程

OAuth 2.0的Access Token不一定必须是JWT。

JWT也不等于OAuth 2.0。


四十七、JWT在微服务中的典型架构

一个典型微服务系统:

                    ┌──────────────┐
                    │   认证服务    │
                    └──────┬───────┘
                           │
                       JWT Token
                           │
                           ↓
                     ┌───────────┐
                     │ API Gateway│
                     └─────┬─────┘
                           │
             ┌─────────────┼─────────────┐
             ↓             ↓             ↓
        用户服务        订单服务        商品服务

用户登录:

客户端
 ↓
认证服务
 ↓
Access Token
+
Refresh Token

访问API:

客户端
 ↓
Gateway
 ↓
验证JWT
 ↓
业务服务

对于复杂系统,可以进一步设计:

  • 认证中心
  • API Gateway
  • 权限中心
  • Token黑名单
  • Refresh Token管理
  • 公钥服务
  • 密钥轮换机制

四十八、JWT在API接口中的使用

例如登录接口:

POST /api/login
Content-Type: application/json

请求:

{
  "username": "tom",
  "password": "password"
}

服务器返回:

{
  "accessToken": "xxxxx",
  "refreshToken": "yyyyy",
  "expiresIn": 900
}

之后调用用户接口:

GET /api/user/profile
Authorization: Bearer xxxxx

服务器验证:

Signature
+
exp
+
iss
+
aud
+
权限

验证成功:

{
  "id": 10001,
  "username": "tom"
}

四十九、JWT安全开发流程

开发JWT认证功能时,可以按照以下流程设计。

第一步:设计登录接口

POST /login

完成:

  • 用户名验证
  • 密码验证
  • 账号状态检查

第二步:生成Access Token

包含必要Claims:

sub
iat
exp
iss
© 2026 IYA工作室