当前位置: 首页 > news >正文

API接口安全-1:身份认证之传统Token VS JWT

在Web应用开发中,身份认证是保障系统安全的核心环节。从早期的Session认证到如今的Token机制,技术不断演进以适应分布式、跨域场景的需求。其中,传统TokenJWT(JSON Web Token) 是两种主流方案,但二者在实现原理、适用场景上有显著差异。本文将分析两者的工作机制、优缺点及核心区别。

一、传统Token:依赖存储的“凭证式”认证

1. 什么是传统Token?

传统Token(令牌)是服务器颁发给客户端的“访问凭证”,本质是一串随机字符串(如UUID)。客户端通过Token证明自己的身份,服务器通过验证Token的有效性(是否存在、是否过期)决定是否允许访问资源。

2. 工作原理:“存储-校验”的中心化流程

传统Token的认证流程可概括为“生成-存储-验证”三步,核心依赖外部存储(如Redis、数据库)记录Token状态:

认证流程详解
  1. 用户登录:前端提交账号密码,服务器验证通过后,生成唯一Token(如a1b2c3d4-5678-90ef-ghij-klmnopqrstuv);
  2. 存储Token:服务器将Token作为key,用户ID(或用户信息)作为value,存入Redis(或数据库),并设置过期时间(如2小时);
  3. 客户端存储Token:服务器返回Token给客户端,客户端将其存入Cookie或LocalStorage;
  4. 后续请求验证:客户端每次请求时,在Header(如Authorization: Bearer <token>)中携带Token;
  5. 服务器校验:服务器从Redis中查询Token:
    • 若不存在(或已过期):拒绝访问,返回“未登录”;
    • 若存在:从value中获取用户ID,查询数据库确认用户信息,验证通过后返回资源。
传统Token流程图解
客户端 服务器 Redis 数据库 登录阶段 1. 登录请求 (账号密码) 2. 生成Token (存储) 3. 存储Token (确认) 4. 返回Token 后续请求阶段 5. 请求+Token (业务接口调用) 6. 查询Token (验证有效性) 7. 验证存在 (返回用户ID) 8. 查询用户信息 (根据用户ID) 9. 返回资源 (业务数据) 客户端 服务器 Redis 数据库

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:签名算法(如HS256RS256);typ:令牌类型(固定为JWT

经Base64Url编码后形成第一部分。

(2)Payload(载荷)

存储用户声明信息,分三类:

  • 标准声明(可选):如iss(签发者)、exp(过期时间)、sub(用户ID)、iat(签发时间);
  • 公共声明:自定义公开信息(如usernamerole);
  • 私有声明:客户端与服务器约定的敏感信息(需加密)。
    示例:
{ "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认证流程详解
  1. 用户登录:前端提交账号密码,服务器验证通过后,生成Header和Payload;
  2. 生成签名:用服务器密钥(Secret)对Header和Payload进行签名,拼接为JWT返回给客户端;
  3. 客户端存储JWT:存入Cookie或LocalStorage;
  4. 后续请求验证:客户端请求时携带JWT,服务器接收后:
    • 分离Header、Payload、Signature;
    • 用相同密钥和算法重新计算签名,对比是否与接收的Signature一致(验证未篡改);
    • 检查Payload中的exp(过期时间)、iss(签发者)等声明(验证有效性);
    • 验证通过后,直接从Payload中提取用户信息(如sub用户ID),无需查询数据库。
客户端 服务器 1. 登录请求(账号密码) 2. 验证身份\n3. 生成Header+Payload并签名生成JWT 4. 返回JWT 5. 后续API请求(携带JWT) 6. 验证JWT签名及声明(如exp, iss) 7. 返回请求资源 客户端 服务器

4. 优缺点:无状态高效,但灵活性受限

优点
  • 无状态:服务器无需存储Token,减轻Redis/数据库压力,支持高并发和水平扩展;
  • 自包含:Payload携带用户信息,减少数据库查询,提升响应速度;
  • 跨语言/跨域:基于JSON和Base64,支持多语言解析,适用于分布式系统和跨域场景。
缺点
  • 无法主动吊销:JWT生成后无法修改,若需强制登出(如账号被盗),需等待Token自然过期(或结合黑名单机制,违背无状态初衷);
  • Payload安全风险:Base64编码可逆,若存放敏感信息(如手机号),需用非对称加密(如RS256);
  • Token体积较大:Payload信息越多,JWT越长,可能增加网络传输开销。

三、传统Token与JWT的核心区别

对比维度传统TokenJWT
本质随机字符串凭证包含声明的自验证令牌
存储依赖依赖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状态(支持吊销)。最终,选型的核心是平衡安全性、性能与业务需求——没有最好的方案,只有最适合的方案。

http://www.dtcms.com/a/263536.html

相关文章:

  • 【甲方安全建设】SDL基线建设及审计评估
  • 从设计到开发一个小程序页面
  • JavaScript异步编程的五种方式
  • RocketMQ第五节(springboot整合MQ)
  • C语言main函数的原理:程序入口的奥秘
  • docker使用容器网络
  • 华为云Flexus+DeepSeek征文 | 对接华为云ModelArts Studio大模型:AI赋能投资理财分析与决策
  • 【软考高项论文】信息系统项目的人力资源管理
  • springboot中多个定时任务(@Scheduled)如何互不影响
  • P1967 [NOIP 2013 提高组] 货车运输
  • localStorage 和 sessionStorage
  • 编译原理——运行时存储组织与内存管理
  • Zookeeper安装使用教程
  • 回写缓存为何需要脏位?
  • SimLOD代码精读(二)建立Octree之Splitting Pass分裂阶段
  • 英国研究团队启动合成完整人类基因组的前沿项目
  • Java-包-访问修饰符-封装
  • Redis Lua 调试器(LDB)完全指南
  • 深度剖析 LNK 参数隐藏攻击 (ZDI-CAN-25373)
  • C++ Vector的使用(下)
  • 贪心算法在C++中的应用与实践
  • 基于动漫数据的可视化分析与推荐系统实现
  • Pyhton-EXCEL与Mysql数据对比
  • Monorepo+Pnpm+Turborepo
  • Vue Vue-route (1)
  • jvm的调优命令jstack打印堆栈信息阐述以及调优
  • Linux信号量
  • 基础算法合集-图论
  • 《AI的“三体进化”:数字基因与超人类思维的奇点降临》
  • Windows 11 24H2更新系统后WiFi不显示故障处理