API接口安全-1:身份认证之传统Token VS JWT
在Web应用开发中,身份认证是保障系统安全的核心环节。从早期的Session认证到如今的Token机制,技术不断演进以适应分布式、跨域场景的需求。其中,传统Token和JWT(JSON Web Token) 是两种主流方案,但二者在实现原理、适用场景上有显著差异。本文将分析两者的工作机制、优缺点及核心区别。
一、传统Token:依赖存储的“凭证式”认证
1. 什么是传统Token?
传统Token(令牌)是服务器颁发给客户端的“访问凭证”,本质是一串随机字符串(如UUID)。客户端通过Token证明自己的身份,服务器通过验证Token的有效性(是否存在、是否过期)决定是否允许访问资源。
2. 工作原理:“存储-校验”的中心化流程
传统Token的认证流程可概括为“生成-存储-验证”三步,核心依赖外部存储(如Redis、数据库)记录Token状态:
认证流程详解:
- 用户登录:前端提交账号密码,服务器验证通过后,生成唯一Token(如
a1b2c3d4-5678-90ef-ghij-klmnopqrstuv
); - 存储Token:服务器将Token作为
key
,用户ID(或用户信息)作为value
,存入Redis(或数据库),并设置过期时间(如2小时); - 客户端存储Token:服务器返回Token给客户端,客户端将其存入Cookie或LocalStorage;
- 后续请求验证:客户端每次请求时,在Header(如
Authorization: Bearer <token>
)中携带Token; - 服务器校验:服务器从Redis中查询Token:
- 若不存在(或已过期):拒绝访问,返回“未登录”;
- 若存在:从
value
中获取用户ID,查询数据库确认用户信息,验证通过后返回资源。
传统Token流程图解:
3. 优缺点:分布式友好,但依赖存储
优点:
- 隐藏敏感数据:Token本身是随机字符串,不携带用户信息,避免明文传输风险;
- 分布式支持:通过Redis等中心化存储,解决Session共享问题,适用于多服务器集群;
- 灵活可控:可随时在Redis中删除Token(主动吊销),支持强制登出、账号锁定等场景。
缺点:
- 依赖外部存储:每次请求需查询Redis/数据库,增加IO开销,高并发场景可能成为瓶颈;
- 性能损耗:存储和查询Token的过程会增加服务器响应时间;
- 架构复杂度:需维护Redis等存储服务,增加系统部署和运维成本。
二、JWT:自包含的“声明式”认证
1. 什么是JWT?
JWT(JSON Web Token)是一种紧凑、自包含的轻量级令牌格式,通过JSON对象存储用户声明信息(如用户ID、角色、过期时间),并通过数字签名保证信息完整性。服务器无需存储Token,仅通过验证签名即可确认身份。
2. 核心组成:三部分构成的“完整凭证”
JWT由三部分组成,用.
分隔,每部分均为Base64编码的字符串:
Header.Payload.Signature
# 示例:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
(1)Header(头部)
声明Token类型和签名算法,如:
{ "alg": "HS256", "typ": "JWT" }
# alg:签名算法(如HS256、RS256);typ:令牌类型(固定为JWT)
经Base64Url编码后形成第一部分。
(2)Payload(载荷)
存储用户声明信息,分三类:
- 标准声明(可选):如
iss
(签发者)、exp
(过期时间)、sub
(用户ID)、iat
(签发时间); - 公共声明:自定义公开信息(如
username
、role
); - 私有声明:客户端与服务器约定的敏感信息(需加密)。
示例:
{ "sub": "123456", "username": "zhangsan", "exp": 1717267200 }
经Base64Url编码后形成第二部分(注意:Base64编码可逆,请勿存放敏感信息!)。
(3)Signature(签名)
通过Header指定的算法(如HS256),对编码后的Header、Payload和服务器密钥(Secret)进行加密,生成签名:
Signature = HS256( Base64UrlEncode(Header) + "." + Base64UrlEncode(Payload), Secret )
签名用于验证Token是否被篡改,是JWT安全的核心。
3. 工作原理:“生成-签名-验证”的无状态流程
JWT的认证流程无需外部存储,核心是通过签名确保信息可信:
JWT认证流程详解:
- 用户登录:前端提交账号密码,服务器验证通过后,生成Header和Payload;
- 生成签名:用服务器密钥(Secret)对Header和Payload进行签名,拼接为JWT返回给客户端;
- 客户端存储JWT:存入Cookie或LocalStorage;
- 后续请求验证:客户端请求时携带JWT,服务器接收后:
- 分离Header、Payload、Signature;
- 用相同密钥和算法重新计算签名,对比是否与接收的Signature一致(验证未篡改);
- 检查Payload中的
exp
(过期时间)、iss
(签发者)等声明(验证有效性); - 验证通过后,直接从Payload中提取用户信息(如
sub
用户ID),无需查询数据库。
4. 优缺点:无状态高效,但灵活性受限
优点:
- 无状态:服务器无需存储Token,减轻Redis/数据库压力,支持高并发和水平扩展;
- 自包含:Payload携带用户信息,减少数据库查询,提升响应速度;
- 跨语言/跨域:基于JSON和Base64,支持多语言解析,适用于分布式系统和跨域场景。
缺点:
- 无法主动吊销:JWT生成后无法修改,若需强制登出(如账号被盗),需等待Token自然过期(或结合黑名单机制,违背无状态初衷);
- Payload安全风险:Base64编码可逆,若存放敏感信息(如手机号),需用非对称加密(如RS256);
- Token体积较大:Payload信息越多,JWT越长,可能增加网络传输开销。
三、传统Token与JWT的核心区别
对比维度 | 传统Token | JWT |
---|---|---|
本质 | 随机字符串凭证 | 包含声明的自验证令牌 |
存储依赖 | 依赖Redis/数据库存储 | 无需存储(自包含) |
验证方式 | 查询存储系统确认有效性 | 本地验证签名和声明 |
状态性 | 有状态(需记录Token状态) | 无状态(Token自带所有信息) |
灵活性 | 支持主动吊销、动态过期 | 无法修改,过期不可控 |
性能 | 依赖IO查询,性能损耗较高 | 本地计算验证,性能更优 |
适用场景 | 需要频繁吊销、权限动态变化 | 无状态、高并发、跨域认证 |
四、选型建议:如何选择适合的认证方案?
选传统Token:
- 主动控制令牌生命周期:如电商平台“踢下线”功能、管理员强制登出;
- 动态权限管理:用户权限变更需实时生效(传统Token可通过Redis更新value中的权限信息);
- 敏感操作保护:结合Redis实现Token黑名单(如检测异常登录时临时封禁)。
选JWT:
- 无状态架构:微服务、分布式系统,避免存储依赖(如K8s集群中的多节点认证);
- 高并发场景:减少数据库/Redis查询,提升接口响应速度(如秒杀系统、API网关认证);
- 跨域/跨服务认证:前后端分离、多端应用(Web/APP/小程序)共享认证状态。
传统Token和JWT并非对立关系,而是针对不同场景的技术选择。传统Token通过“存储-校验”保证灵活性,适合需要动态控制的场景;JWT通过“自包含-签名验证”实现无状态高效,适合高并发、分布式架构。
实际开发中,甚至可结合两者优势:用JWT作为传统Token的“载体”,将用户基本信息存入JWT(减少查询),同时在Redis中记录Token状态(支持吊销)。最终,选型的核心是平衡安全性、性能与业务需求——没有最好的方案,只有最适合的方案。