讲师中心 微信公众号
AI工具推荐 视频效率加速

JWT令牌生成与验证详细实现教程

风辰吖_3933

风辰吖_3933

发布时间:2025-07-08 14:16:02

|

707人浏览过

|

来源于php中文网

原创

jwt是一种用于身份验证和信息交换的紧凑型令牌,其核心是三段式结构:头部、载荷和签名。生成时将头部与载荷base64url编码后用密钥签名,验证时解析各部分并比对签名及检查声明。使用python的pyjwt库可便捷实现生成与验证流程。1. 生成jwt需定义密钥、构造含用户信息及声明的载荷,并使用hs256算法编码;2. 验证jwt则通过解码函数校验签名、过期时间、签发者与接收者等选项。实际应用中,jwt在登录后返回客户端,后续请求通过http头携带令牌完成无状态认证。安全方面应选择合适算法(如hs256或rs256),妥善管理密钥,并遵循最佳实践包括设置合理过期时间、验证iss/aud字段、避免弱密钥、防止alg篡改、处理令牌撤销问题及安全存储令牌。

JWT令牌生成与验证详细实现教程

JWT,全称JSON Web Token,本质上就是一种紧凑、自包含的、用于在网络各方之间安全传输信息的方式。它通常被用来进行身份验证和信息交换,特别是在无状态API的设计中,它简直是核心组件。说白了,它就是一张加了密(或者说加了签)的“通行证”,里面包含了你的身份信息和一些权限声明,服务器不用每次都去查数据库,只要验证这张通行证的真伪和有效期就行了。

JWT令牌生成与验证详细实现教程

解决方案

要实现JWT的生成与验证,核心在于理解其三段式结构:头部(Header)、载荷(Payload)和签名(Signature)。生成时,我们把头部和载荷用Base64Url编码后,用一个密钥进行签名,然后把这三部分用点号连接起来。验证时,则反过来,解析出各部分,用相同的密钥重新计算签名,并与接收到的签名比对,同时还要检查载荷中的声明(比如过期时间)。

以Python为例,使用PyJWT库是目前最常见也最便捷的方式:

JWT令牌生成与验证详细实现教程

1. 生成JWT令牌

import jwt
import datetime
import time

# 定义一个秘密密钥,非常重要,必须妥善保管
SECRET_KEY = "这是一个超级安全的秘密密钥,请务必替换掉!" # 实际应用中应从环境变量或配置服务中读取

def generate_jwt_token(user_id: str, username: str, expires_in_minutes: int = 30):
    """
    生成一个JWT令牌。
    :param user_id: 用户唯一标识符
    :param username: 用户名
    :param expires_in_minutes: 令牌过期时间(分钟)
    :return: 生成的JWT字符串
    """
    try:
        # 头部 (Header) 通常由库自动处理,指定算法和类型
        # 载荷 (Payload) 包含声明信息
        payload = {
            "user_id": user_id,
            "username": username,
            "iss": "your_service_name",  # Issuer (签发者)
            "aud": "your_client_app",    # Audience (接收者)
            "iat": datetime.datetime.utcnow(), # Issued At (签发时间)
            "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=expires_in_minutes) # Expiration Time (过期时间)
        }
        # 使用HS256算法和秘密密钥进行编码
        token = jwt.encode(payload, SECRET_KEY, algorithm="HS256")
        return token
    except Exception as e:
        print(f"生成JWT时发生错误: {e}")
        return None

# 示例生成
# token = generate_jwt_token("12345", "Alice", 60)
# if token:
#     print(f"生成的JWT: {token}")

2. 验证JWT令牌

JWT令牌生成与验证详细实现教程
import jwt
from jwt.exceptions import ExpiredSignatureError, InvalidTokenError

# 秘密密钥与生成时保持一致
SECRET_KEY = "这是一个超级安全的秘密密钥,请务必替换掉!"

def verify_jwt_token(token: str):
    """
    验证一个JWT令牌。
    :param token: 要验证的JWT字符串
    :return: 验证成功返回载荷字典,失败返回None
    """
    try:
        # 解码并验证令牌。verify_exp=True 默认会检查过期时间
        # verify_iss 和 verify_aud 也可以设置为 True 来验证签发者和接收者
        payload = jwt.decode(token, SECRET_KEY, algorithms=["HS256"],
                             options={"verify_signature": True, "verify_exp": True,
                                      "verify_iss": True, "verify_aud": True},
                             issuer="your_service_name", # 验证签发者
                             audience="your_client_app") # 验证接收者
        return payload
    except ExpiredSignatureError:
        print("JWT令牌已过期。")
        return None
    except InvalidTokenError as e:
        print(f"JWT令牌无效: {e}")
        return None
    except Exception as e:
        print(f"验证JWT时发生未知错误: {e}")
        return None

# 示例验证
# # 假设我们有一个token
# # verified_payload = verify_jwt_token(token)
# # if verified_payload:
# #     print(f"验证成功,载荷: {verified_payload}")

实际应用中,你会在用户登录成功后生成JWT并返回给客户端,客户端在后续请求中将此令牌放入HTTP请求头(通常是Authorization: Bearer <token></token>)发送给服务器,服务器接收到请求后,再调用验证函数来确认请求的合法性。

JWT的结构与组成部分:它到底长什么样?

初次接触JWT,大家可能都会好奇,这玩意儿不就是一串看起来毫无规律的字符串吗?它到底是怎么做到自包含的?说实话,它比你想象的要简单得多,但又足够巧妙。一个完整的JWT字符串通常由三部分组成,它们之间用点(.)分隔开:

Header.Payload.Signature

  1. 头部(Header): 这部分通常是一个JSON对象,描述了JWT的元数据,比如使用的签名算法(alg)和令牌类型(typ)。最常见的头部可能长这样:

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

    alg 表示签名算法,比如HMAC SHA256(HS256)或者RSA SHA256(RS256)。typ 表示令牌类型,这里固定是JWT。这个JSON对象会先被Base64Url编码。

  2. 载荷(Payload): 这是JWT的真正“肉身”,包含了我们想要传输的实际信息,也就是所谓的“声明(claims)”。声明又分为三种:

    • 注册声明(Registered Claims): 这些是预定义的一些声明,不是强制性的,但推荐使用,比如 iss (issuer, 签发者), exp (expiration time, 过期时间), sub (subject, 主题), aud (audience, 接收者) 等。它们提供了互操作性。
    • 公共声明(Public Claims): 你可以定义自己的声明,但为了避免冲突,建议使用IANA JSON Web Token Registry中定义的公共声明。
    • 私有声明(Private Claims): 这是你和使用方之间私下约定的一些声明,比如上面示例中的 user_id 和 username。 载荷也是一个JSON对象,同样会被Base64Url编码。
  3. 签名(Signature): 这是JWT安全性的核心。它是由编码后的头部、编码后的载荷,以及一个秘密密钥(或私钥)通过指定算法计算出来的哈希值。它的作用是验证发送者身份以及确保令牌在传输过程中没有被篡改。 计算签名的伪代码大致是这样: HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) 这个签名部分确保了,一旦令牌的任何一个字节被修改,验证时签名就会不匹配,从而使令牌失效。

当这三部分都准备好并编码后,它们就用点号连接起来,形成最终的JWT字符串。正是这种结构,让JWT能够自包含地传递信息,而无需服务器频繁查询数据库,从而实现了无状态认证。

Python Hardener
Python Hardener

通过完整的安全审查、代码整洁重构、健壮的错误处理、正确的日志记录以及完整的文档字符串覆盖,强化 Python 脚本。

下载

选择合适的签名算法与密钥管理:安全性的核心考量

在JWT的生成与验证中,签名算法和密钥管理是至关重要的环节,直接关系到整个系统的安全性。我个人觉得,这就像你给你的“通行证”盖章,章的样式(算法)和保管章的人(密钥)都得靠谱,不然这通行证就形同虚设了。

签名算法的选择:对称还是非对称?

  1. HMAC SHA256 (HS256) - 对称加密算法:

    • 原理: 这种算法使用同一个秘密密钥(SECRET_KEY)来生成签名和验证签名。
    • 适用场景: 通常用于单一服务或紧密耦合的服务之间。例如,一个后端服务既负责签发令牌,也负责验证令牌。或者在微服务架构中,所有需要验证JWT的服务都共享同一个秘密密钥(但这要求密钥分发和保管非常安全)。
    • 优点: 实现简单,性能较高。
    • 缺点: 密钥泄露风险较高,因为同一个密钥既用于签名也用于验证。一旦密钥泄露,攻击者就能伪造令牌。
  2. RSA SHA256 (RS256) - 非对称加密算法:

    • 原理: 这种算法使用一对密钥:一个私钥(Private Key)用于签名,一个公钥(Public Key)用于验证。私钥必须严格保密,而公钥可以公开。
    • 适用场景: 适用于分布式系统或第三方服务需要验证JWT的场景。例如,一个认证服务使用私钥签发令牌,而多个资源服务(API网关、微服务A、微服务B)只需要使用公钥来验证令牌的有效性,它们无需知道私钥。
    • 优点: 安全性更高,私钥泄露风险降低,因为公钥即使泄露也无法用于签名。
    • 缺点: 实现相对复杂,性能略低于对称算法(虽然在大多数Web应用中影响不大)。密钥管理(如证书轮换)也更复杂。

在我看来,如果你只是一个简单的应用,或者所有服务都在你的严格控制之下,HS256已经足够了,因为它简单高效。但如果你的系统越来越庞大,涉及到跨域、多服务、甚至第三方集成,那么RS256的优势就会凸显出来,它能更好地隔离签发方和验证方,降低整体风险。

密钥管理:这才是真正的挑战!

无论你选择哪种算法,密钥的生成、存储、分发和轮换都是重中之重。这就像你拥有了世界上最安全的印章,但如果把印章随意丢在桌上,那安全就无从谈起了。

  • 生成: 密钥必须是足够长、足够随机的字符串(对于HS256)或者安全的私钥/公钥对。不要使用硬编码在代码里的弱密钥。
  • 存储: 绝不能将密钥直接硬编码在代码中,也不应该提交到版本控制系统。最佳实践是将密钥存储在:
    • 环境变量中。
    • 专门的密钥管理服务(如AWS KMS, Azure Key Vault, HashiCorp Vault)。
    • 安全的配置文件中(并确保配置文件本身不被泄露)。
  • 分发: 如果是HS256,所有需要验证的服务都必须安全地获取到这个密钥。如果是RS256,私钥只存在于签发方,公钥可以安全地分发给验证方。
  • 轮换: 密钥不应该永久不变。定期轮换密钥是一种重要的安全实践,它可以限制密钥泄露的潜在损害。比如,每隔一段时间就生成一个新的密钥,并逐步淘汰旧的密钥。这通常需要一个复杂的策略来处理新旧令牌的兼容性问题。

说实话,密钥管理是很多初学者容易忽略但又极其关键的一环。一个再完美的JWT实现,如果密钥管理出了问题,那一切都是白搭。

JWT的常见安全陷阱与最佳实践:如何避免踩坑?

JWT虽然强大且流行,但它并非银弹。在使用过程中,如果不注意一些细节,很容易掉进一些常见的安全陷阱。我总觉得,任何技术都有两面性,用得好是利器,用不好就是坑。

  1. 不验证过期时间(exp): 这是最常见也最致命的错误。如果JWT没有设置过期时间,或者在验证时忽略了exp字段,那么即使令牌被盗,它也能被无限期使用。

    • 最佳实践: 始终设置一个合理的过期时间(通常几分钟到几小时),并在验证时强制检查exp。PyJWT等库默认会做这个检查,但要确保你没有禁用它。对于长时间会话,考虑使用短命的访问令牌(Access Token)配合长命的刷新令牌(Refresh Token)。
  2. 不验证签发者(iss)和接收者(aud): 如果你有多个服务签发JWT,或者你的JWT可能被多个客户端接收,那么验证iss和aud可以防止“跨应用”的令牌滥用。例如,一个为你的内部服务A签发的令牌,不应该能被服务B接受。

    • 最佳实践: 在生成和验证时都明确指定并检查iss和aud。这就像给你的通行证指定了“有效区域”,防止它在不该出现的地方被使用。
  3. 使用弱密钥或硬编码密钥: 前面已经强调过,密钥是JWT安全的核心。一个简单的、可预测的密钥,或者直接写死在代码里的密钥,一旦被反编译或代码泄露,整个系统的安全性就荡然无存。

    • 最佳实践: 使用足够长、足够随机的密钥。将密钥存储在环境变量、密钥管理服务或安全配置中,绝不提交到版本控制系统。
  4. alg 算法可篡改漏洞: 这是一个比较高级但真实存在的漏洞。攻击者可能会尝试修改JWT头部中的alg字段,将其从一个安全的算法(如HS256)改为none(表示无签名),然后重新构造一个没有签名的JWT。如果你的验证逻辑没有强制检查alg字段或允许none算法,攻击者就能绕过签名验证。

    • 最佳实践: 在验证时,明确指定允许的算法列表(例如algorithms=["HS256"]),并且绝不允许none算法。这是库的默认行为,但务必不要自己去修改或放松这个限制。
  5. 令牌撤销(Revocation)问题: JWT是无状态的,一旦签发,在过期之前,服务器无法主动使其失效。这意味着如果一个用户登出,或者其权限被撤销,已签发的JWT仍然有效,直到它过期。

    • 最佳实践:
      • 短过期时间 + 刷新令牌: 这是最常见的解决方案。访问令牌过期时间短,一旦用户登出或权限变更,等待几分钟到几小时,令牌就自动失效。刷新令牌用于获取新的访问令牌,刷新令牌本身可以存储在数据库中,并进行更严格的验证和撤销管理。
      • 黑名单/白名单: 对于需要立即撤销的场景,可以在服务器端维护一个已撤销JWT的黑名单(或有效JWT的白名单)。每次验证时都查一下这个列表。但这会引入状态,削弱JWT的无状态优势,并增加数据库查询开销。
      • 更改密钥: 如果是全局性的紧急撤销(如密钥泄露),可以更换签名密钥,这将导致所有旧令牌失效。
  6. 在客户端存储JWT不当: 将JWT存储在localStorage或sessionStorage中,虽然方便,但容易受到XSS(跨站脚本攻击)的攻击,攻击者可以通过恶意脚本窃取令牌。

    • 最佳实践:
      • 对于Web应用,将JWT存储在HttpOnly的Secure cookie中。HttpOnly可以防止JavaScript访问cookie,Secure确保cookie只通过HTTPS发送。
      • 对于移动应用或桌面应用,将其存储在操作系统的安全存储区域(如iOS的Keychain,Android的Keystore)。

理解这些陷阱并采纳最佳实践,能让你的JWT实现真正地安全和健壮。毕竟,技术是死的,人是活的,关键在于我们如何去运用它。

热门AI工具

更多
UpDream
UpDream Hot

一款AI视频创作工具,主要用于哔哩哔哩推出的自研AI视频创作工具,适合需要提升相关任务效率的用户。

花生AI
花生AI Hot

一款AI视频创作工具,主要用于B站推出的AI视频创作工具,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

PixPix
PixPix Hot

PixPix是一款面向电商视觉生产的AI商品图生成工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

Flowith
Flowith Hot

Flowith是一款基于画布和智能体工作流的 AI 协作工作空间。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

2153

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

3514

2023.10.07

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2255

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

3642

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

1216

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

4199

2025.09.10

cookie
cookie

Cookie 是一种在用户计算机上存储小型文本文件的技术,用于在用户与网站进行交互时收集和存储有关用户的信息。当用户访问一个网站时,网站会将一个包含特定信息的 Cookie 文件发送到用户的浏览器,浏览器会将该 Cookie 存储在用户的计算机上。之后,当用户再次访问该网站时,浏览器会向服务器发送 Cookie,服务器可以根据 Cookie 中的信息来识别用户、跟踪用户行为等。

12599

2023.06.30

document.cookie获取不到怎么解决
document.cookie获取不到怎么解决

document.cookie获取不到的解决办法:1、浏览器的隐私设置;2、Same-origin policy;3、HTTPOnly Cookie;4、JavaScript代码错误;5、Cookie不存在或过期等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

1286

2023.11.23

DBeaver怎么导入数据
DBeaver怎么导入数据

DBeaver数据导入专题,整理Excel、CSV、SQL、TXT等常见文件的数据导入方法,涵盖目标数据库选择、数据表创建、字段映射、数据类型设置及导入参数配置等操作,帮助用户将本地文件中的数据写入数据库,并了解批量导入、重复数据处理和导入失败等常见问题的解决方法。

0

2026.11.05

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn