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

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

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

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令牌

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>)发送给服务器,服务器接收到请求后,再调用验证函数来确认请求的合法性。
初次接触JWT,大家可能都会好奇,这玩意儿不就是一串看起来毫无规律的字符串吗?它到底是怎么做到自包含的?说实话,它比你想象的要简单得多,但又足够巧妙。一个完整的JWT字符串通常由三部分组成,它们之间用点(.)分隔开:
Header.Payload.Signature
头部(Header):
这部分通常是一个JSON对象,描述了JWT的元数据,比如使用的签名算法(alg)和令牌类型(typ)。最常见的头部可能长这样:
{
"alg": "HS256",
"typ": "JWT"
}alg 表示签名算法,比如HMAC SHA256(HS256)或者RSA SHA256(RS256)。typ 表示令牌类型,这里固定是JWT。这个JSON对象会先被Base64Url编码。
载荷(Payload): 这是JWT的真正“肉身”,包含了我们想要传输的实际信息,也就是所谓的“声明(claims)”。声明又分为三种:
iss (issuer, 签发者), exp (expiration time, 过期时间), sub (subject, 主题), aud (audience, 接收者) 等。它们提供了互操作性。user_id 和 username。
载荷也是一个JSON对象,同样会被Base64Url编码。签名(Signature):
这是JWT安全性的核心。它是由编码后的头部、编码后的载荷,以及一个秘密密钥(或私钥)通过指定算法计算出来的哈希值。它的作用是验证发送者身份以及确保令牌在传输过程中没有被篡改。
计算签名的伪代码大致是这样:
HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)
这个签名部分确保了,一旦令牌的任何一个字节被修改,验证时签名就会不匹配,从而使令牌失效。
当这三部分都准备好并编码后,它们就用点号连接起来,形成最终的JWT字符串。正是这种结构,让JWT能够自包含地传递信息,而无需服务器频繁查询数据库,从而实现了无状态认证。
在JWT的生成与验证中,签名算法和密钥管理是至关重要的环节,直接关系到整个系统的安全性。我个人觉得,这就像你给你的“通行证”盖章,章的样式(算法)和保管章的人(密钥)都得靠谱,不然这通行证就形同虚设了。
签名算法的选择:对称还是非对称?
HMAC SHA256 (HS256) - 对称加密算法:
SECRET_KEY)来生成签名和验证签名。RSA SHA256 (RS256) - 非对称加密算法:
在我看来,如果你只是一个简单的应用,或者所有服务都在你的严格控制之下,HS256已经足够了,因为它简单高效。但如果你的系统越来越庞大,涉及到跨域、多服务、甚至第三方集成,那么RS256的优势就会凸显出来,它能更好地隔离签发方和验证方,降低整体风险。
密钥管理:这才是真正的挑战!
无论你选择哪种算法,密钥的生成、存储、分发和轮换都是重中之重。这就像你拥有了世界上最安全的印章,但如果把印章随意丢在桌上,那安全就无从谈起了。
说实话,密钥管理是很多初学者容易忽略但又极其关键的一环。一个再完美的JWT实现,如果密钥管理出了问题,那一切都是白搭。
JWT虽然强大且流行,但它并非银弹。在使用过程中,如果不注意一些细节,很容易掉进一些常见的安全陷阱。我总觉得,任何技术都有两面性,用得好是利器,用不好就是坑。
不验证过期时间(exp):
这是最常见也最致命的错误。如果JWT没有设置过期时间,或者在验证时忽略了exp字段,那么即使令牌被盗,它也能被无限期使用。
exp。PyJWT等库默认会做这个检查,但要确保你没有禁用它。对于长时间会话,考虑使用短命的访问令牌(Access Token)配合长命的刷新令牌(Refresh Token)。不验证签发者(iss)和接收者(aud):
如果你有多个服务签发JWT,或者你的JWT可能被多个客户端接收,那么验证iss和aud可以防止“跨应用”的令牌滥用。例如,一个为你的内部服务A签发的令牌,不应该能被服务B接受。
iss和aud。这就像给你的通行证指定了“有效区域”,防止它在不该出现的地方被使用。使用弱密钥或硬编码密钥: 前面已经强调过,密钥是JWT安全的核心。一个简单的、可预测的密钥,或者直接写死在代码里的密钥,一旦被反编译或代码泄露,整个系统的安全性就荡然无存。
alg 算法可篡改漏洞:
这是一个比较高级但真实存在的漏洞。攻击者可能会尝试修改JWT头部中的alg字段,将其从一个安全的算法(如HS256)改为none(表示无签名),然后重新构造一个没有签名的JWT。如果你的验证逻辑没有强制检查alg字段或允许none算法,攻击者就能绕过签名验证。
algorithms=["HS256"]),并且绝不允许none算法。这是库的默认行为,但务必不要自己去修改或放松这个限制。令牌撤销(Revocation)问题: JWT是无状态的,一旦签发,在过期之前,服务器无法主动使其失效。这意味着如果一个用户登出,或者其权限被撤销,已签发的JWT仍然有效,直到它过期。
在客户端存储JWT不当:
将JWT存储在localStorage或sessionStorage中,虽然方便,但容易受到XSS(跨站脚本攻击)的攻击,攻击者可以通过恶意脚本窃取令牌。
HttpOnly的Secure cookie中。HttpOnly可以防止JavaScript访问cookie,Secure确保cookie只通过HTTPS发送。理解这些陷阱并采纳最佳实践,能让你的JWT实现真正地安全和健壮。毕竟,技术是死的,人是活的,关键在于我们如何去运用它。
以上就是JWT令牌生成与验证详细实现教程的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号