news 2026/4/16 17:47:11

一篇看懂JWT:Web安全的“身份证”

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
一篇看懂JWT:Web安全的“身份证”

诸神缄默不语-个人技术博文与视频目录

文章目录

  • 什么是JWT?一个简单的比喻
  • 为什么需要JWT?
  • JWT长什么样?
    • 1. 头部(Header)
    • 2. 载荷(Payload)
    • 3. 签名(Signature)
  • 用Python玩转JWT
    • 场景1:用户登录后生成JWT
    • 场景2:验证收到的JWT
    • 场景3:完整的登录验证流程
  • JWT的实际应用场景
    • 1. **单点登录(SSO)**
    • 2. **API身份验证**
    • 3. **信息交换**
  • 重要安全注意事项
    • ✅ **应该做的:**
    • ❌ **不要做的:**
  • 常见问题解答
  • 总结

什么是JWT?一个简单的比喻

想象一下你去参加一个大型会议。第一次入场时,工作人员检查你的购票信息,确认无误后给你戴上一个手环。之后在会议期间,你进出各个分会场、领取茶歇、参加活动,只需要亮出手环就可以了,不需要反复出示购票凭证。

JWT(JSON Web Token)就是这个数字世界的“手环”。它是一种让Web应用安全传递信息的方式,解决了“如何证明你是你”的问题。

为什么需要JWT?

在传统网站中,服务器通过“会话”(Session)记录用户登录状态。但这有几个问题:

  1. 服务器需要存储大量会话数据,用户多了内存压力大
  2. 难以扩展,多台服务器之间要同步会话信息
  3. 不适合移动端和API服务

JWT的出现解决了这些问题:信息都存在令牌里,服务器不用存,只需要验证令牌是否有效即可。

JWT长什么样?

一个JWT看起来像这样(实际是一长串,这里折行显示):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ. SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

它由三部分组成,用点(.)分隔:

  • 头部(Header)

  • 载荷(Payload)

  • 签名(Signature)

1. 头部(Header)

就像信封的“说明标签”,告诉别人这个令牌的基本信息:

{"alg":"HS256",// 签名算法:HS256"typ":"JWT"// 类型:JWT}

2. 载荷(Payload)

这是令牌的“核心内容”,存放实际要传递的信息:

{"sub":"1234567890",// 用户ID"name":"John Doe",// 用户名"iat":1516239022,// 签发时间"exp":1516242622// 过期时间}

3. 签名(Signature)

这是最关键的部分!它像“防伪标识”,确保令牌没有被篡改。

签名的生成方式:

HMACSHA256( base64UrlEncode(头部) + "." + base64UrlEncode(载荷), 密钥 )

用Python玩转JWT

让我们通过代码实际体验一下JWT的使用。首先安装必要的库:

pipinstallPyJWT

场景1:用户登录后生成JWT

importjwtimportdatetime# 密钥(重要!实际项目中要从安全的地方获取)SECRET_KEY="my_secret_key_12345"defcreate_jwt(user_id:str,username:str)->str:""" 创建JWT令牌 """# 设置令牌的过期时间(例如:24小时后)expiration=datetime.datetime.now(datetime.timezone.utc)+datetime.timedelta(hours=24)# 构建载荷(Payload)payload={"user_id":user_id,"username":username,"exp":expiration,# 过期时间"iat":datetime.datetime.now(datetime.timezone.utc)# 签发时间}# 生成JWTtoken=jwt.encode(payload,SECRET_KEY,algorithm="HS256")returntoken# 示例:用户登录成功后生成令牌token=create_jwt("user123","张三")print("生成的JWT令牌:")print(token)print("-"*50)

场景2:验证收到的JWT

importjwtfromtypingimportDict# 密钥(重要!实际项目中要从安全的地方获取)SECRET_KEY="my_secret_key_12345"defverify_jwt(token:str)->Dict:""" 验证JWT令牌 返回解码后的数据或抛出异常 """try:# 验证并解码令牌payload=jwt.decode(token,SECRET_KEY,algorithms=["HS256"])return{"valid":True,"data":payload,"message":"令牌有效"}exceptjwt.ExpiredSignatureError:return{"valid":False,"data":None,"message":"令牌已过期"}exceptjwt.InvalidTokenError:return{"valid":False,"data":None,"message":"无效的令牌"}# 示例:验证令牌print("验证令牌结果:")result=verify_jwt(token)# 在这里输入上一节返回的JWT tokenifresult["valid"]:print("✓ 令牌有效")print(f"用户信息:{result['data']}")else:print(f"✗{result['message']}")print("-"*50)

输出:

验证令牌结果: ✓ 令牌有效 用户信息:{'user_id': 'user123', 'username': '张三', 'exp': 1766732992, 'iat': 1766646592}

场景3:完整的登录验证流程

importjwtimportdatetimefromtypingimportDict# 密钥(重要!实际项目中要从安全的地方获取)SECRET_KEY="my_secret_key_12345"defcreate_jwt(user_id:str,username:str)->str:""" 创建JWT令牌 """# 设置令牌的过期时间(例如:24小时后)expiration=datetime.datetime.now(datetime.timezone.utc)+datetime.timedelta(hours=24)# 构建载荷(Payload)payload={"user_id":user_id,"username":username,"exp":expiration,# 过期时间"iat":datetime.datetime.now(datetime.timezone.utc)# 签发时间}# 生成JWTtoken=jwt.encode(payload,SECRET_KEY,algorithm="HS256")returntokendefverify_jwt(token:str)->Dict:""" 验证JWT令牌 返回解码后的数据或抛出异常 """try:# 验证并解码令牌payload=jwt.decode(token,SECRET_KEY,algorithms=["HS256"])return{"valid":True,"data":payload,"message":"令牌有效"}exceptjwt.ExpiredSignatureError:return{"valid":False,"data":None,"message":"令牌已过期"}exceptjwt.InvalidTokenError:return{"valid":False,"data":None,"message":"无效的令牌"}# 模拟用户数据库users_db={"user123":{"password":"password123",# 实际中应该存储哈希值,而不是明文!"username":"张三","role":"user"},"admin001":{"password":"admin_pass","username":"管理员","role":"admin"}}deflogin_and_get_token(user_id:str,password:str):""" 模拟登录过程 """# 1. 检查用户是否存在ifuser_idnotinusers_db:returnNone,"用户不存在"# 2. 验证密码ifusers_db[user_id]["password"]!=password:returnNone,"密码错误"# 3. 生成JWT令牌user_info=users_db[user_id]token=create_jwt(user_id,user_info["username"])returntoken,"登录成功"defaccess_protected_resource(token:str):""" 访问需要权限的资源 """result=verify_jwt(token)ifnotresult["valid"]:returnf"访问被拒绝:{result['message']}"user_data=result["data"]returnf"欢迎{user_data['username']}!您已成功访问受保护资源。"# 模拟完整流程print("=== 完整登录访问流程 ===")# 1. 用户登录print("1. 用户登录...")token,message=login_and_get_token("user123","password123")print(f"登录结果:{message}")iftoken:print(f"获取到的令牌:{token[:50]}...")print("-"*30)# 2. 访问受保护资源print("2. 访问受保护资源...")iftoken:response=access_protected_resource(token)print(response)print("-"*30)# 3. 演示过期令牌print("3. 演示过期令牌...")# 创建一个立即过期的令牌expired_payload={"user_id":"user123","username":"张三","exp":datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(seconds=1),# 1秒前过期"iat":datetime.datetime.now(datetime.timezone.utc)-datetime.timedelta(hours=1)}expired_token=jwt.encode(expired_payload,SECRET_KEY,algorithm="HS256")response=access_protected_resource(expired_token)print(response)

输出:

=== 完整登录访问流程 === 1. 用户登录... 登录结果:登录成功 获取到的令牌:eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkI... ------------------------------ 2. 访问受保护资源... 欢迎 张三!您已成功访问受保护资源。 ------------------------------ 3. 演示过期令牌... 访问被拒绝:令牌已过期

JWT的实际应用场景

1.单点登录(SSO)

用户在一个系统登录后,无需在其他关联系统重新登录。

2.API身份验证

移动App、前端应用调用后端API时携带JWT。

3.信息交换

安全地在各方之间传递信息,因为签名可以验证内容是否被篡改。

重要安全注意事项

应该做的:

  1. 使用HTTPS:防止令牌在传输中被窃取

  2. 设置合理的过期时间:通常几小时到几天

  3. 存储敏感信息要加密:载荷默认只是编码,不是加密!

  4. 密钥要足够复杂:并且定期更换

不要做的:

  1. 不要在JWT中存储密码等敏感信息

  2. 不要将密钥硬编码在代码中

  3. 不要使用弱签名算法

  4. 前端存储要注意XSS攻击(考虑使用HttpOnly Cookie)

常见问题解答

Q:JWT和Session有什么区别?

A:Session把用户状态存在服务器,JWT把状态存在令牌里发给客户端。

Q:JWT被偷了怎么办?

A:就像手环被偷一样,小偷可以冒充你。因此过期时间要短,重要操作需二次验证。

Q:如何让JWT失效?

A:JWT一旦签发,在过期前无法主动失效。解决方案:使用短有效期+刷新令牌机制,或维护一个小的令牌黑名单。

总结

JWT就像数字世界的“身份证+防伪标识”:

  • 头部说明类型和算法

  • 载荷携带实际信息

  • 签名确保不被篡改

它的优点是无状态、易扩展,适合现代分布式应用。但也要注意安全使用,特别是密钥管理和令牌存储。

希望这篇介绍能帮你理解JWT!在实际项目中,合理使用JWT能让你的应用更安全、更高效。


提示:本文示例代码用于学习演示,实际生产环境中需要考虑更多安全因素。建议使用成熟的认证库(如Authlib、Django REST Framework JWT等)来处理复杂的认证场景。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/16 11:11:44

基于SpringBoot框架社区老年人健康杂志阅读分享管理系统(毕设源码+文档)

课题说明本课题聚焦社区老年人健康知识获取与交流分享的核心需求,针对当前老年群体健康读物适配性不足、阅读资源分散、交流渠道匮乏、健康知识传播效率偏低等痛点,设计开发基于SpringBoot框架的社区老年人健康杂志阅读分享管理系统。系统以SpringBoot为…

作者头像 李华
网站建设 2026/4/16 0:27:15

基于SpringBoot特色农产品销售系统(毕设源码+文档)

课题说明本课题聚焦特色农产品产销对接的数字化需求,针对当前特色农产品流通渠道狭窄、品牌推广不足、供需信息不对称、交易流程不规范等痛点,设计开发基于SpringBoot的特色农产品销售系统。系统以SpringBoot为核心后端框架,结合前端主流开发…

作者头像 李华
网站建设 2026/4/16 13:00:03

腾讯三面:100万 QPS 的秒杀,你用 Redis 做限流?CPU 炸了谁负责?

写在开头: 上周跟一个想跳槽腾讯的朋友吃饭,他一脸郁闷。 三面的时候,面试官问了他一个经典的场景题:“双十一大促,某个爆款商品预计每秒有 100 万请求(QPS),库存只有 100 个。请设…

作者头像 李华
网站建设 2026/4/15 16:36:50

【电动机】三相感应电动机TIM间接磁场定向控制IFOC研究附Matlab代码

✅作者简介:热爱科研的Matlab仿真开发者,擅长数据处理、建模仿真、程序设计、完整代码获取、论文复现及科研仿真。🍎 往期回顾关注个人主页:Matlab科研工作室🍊个人信条:格物致知,完整Matlab代码及仿真咨询…

作者头像 李华
网站建设 2026/4/16 5:51:38

JavaScript—— JavaScript 数字处理工具函数

数字处理工具函数 数字处理在各种业务场景中都很常见,尤其是涉及到金额计算、数据显示等方面。 // 添加千分位分隔符 function addThousandSeparator(num, decimals 2) {if (!num && num ! 0) return -;// 实现逻辑...return num.toString().replace(/\B(?…

作者头像 李华
网站建设 2026/4/16 7:03:12

JavaScript —— JavaScript 轮询机制和异步任务处理工具函数详解

在现代前端应用中,我们经常需要定期向服务器发送请求以获取最新数据,这种场景就需要用到轮询机制。本文介绍一种灵活、可靠的轮询工具函数实现方案。 技术难点 如何实现可控的轮询机制(开始、停止、重启) 如何处理轮询过程中的异常情况 如何限制轮询次数防止无限循环 如何…

作者头像 李华