node中使用jsonwebtoken 解析jwt token时报 invalid token的解决办法

本文记录了解决前端使用JWT生成并验证token时,因未移除空格导致的登录失败问题,分享解决方法和踩坑经验。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

出现背景,登录时根据jwt使用生成token,如下图

 

然后登录成功时根据后台返回的token解析jwt令牌,关键部分代码如下

前端点击登录会出现invalid token报错,如下图

就在我百思不得其解百度了各种办法都试了没效果之后,突然发现我没有把token前的空格去掉

解决办法:

此篇仅作为我的踩坑记录

<think>我们参考了引用[1]和引用[2]中关于JWT解析方法。解析一次性Token解析普通JWTToken的方法基本一致,但需要额外检查Token的唯一标识(jti)是否已被使用,以确保其一次性。以下是详细步骤和示例代码:###解析步骤1.**获取并解析Token**使用JWT解析Token,验证签名和有效期(自动验证`exp`字段)。2.**检查重复使用**从解析后的Claims中获取`jti`(唯一标识),查询缓存/数据库判断该`jti`是否已使用过。3.**标记为已使用**若未使用,则立即标记该`jti`为已使用(防止并发请求导致重复使用)。---###Java示例(使用`jjwt`库)```javaimportio.jsonwebtoken.Claims;importio.jsonwebtoken.Jws;importio.jsonwebtoken.Jwts;importio.jsonwebtoken.security.Keys;publicclassJwtParserExample{//密钥(需与生成Token时一致)privatestaticfinalStringSECRET_KEY="your-strong-secret-key-here-1234567890";publicbooleanvalidateOneTimeToken(Stringtoken){try{//1.解析Token并验证签名/有效期Jws<Claims>claimsJws=Jwts.parserBuilder().setSigningKey(Keys.hmacShaKeyFor(SECRET_KEY.getBytes())).build().parseClaimsJws(token);Claimsclaims=claimsJws.getBody();//2.获取jti(唯一标识)Stringjti=claims.getId();//3.检查jti是否已被使用(示例:Redis查询)if(isTokenUsedInRedis(jti)){thrownewException("Token已失效");}//4.标记为已使用(存入Redis,设置略长于Token有效期的过期时间)markTokenUsedInRedis(jti,7*60);//7分钟过期returntrue;}catch(Exceptione){//处理解析失败或重复使用returnfalse;}}//模拟Redis操作(实际需实现)privatebooleanisTokenUsedInRedis(Stringjti){//伪代码:查询Redis中是否存在该jti//returnredis.exists(jti);returnfalse;}privatevoidmarkTokenUsedInRedis(Stringjti,intexpireSeconds){//伪代码:将jti存入Redis,设置过期时间//redis.setex(jti,expireSeconds,"used");}}```---###关键注意事项1.**签名验证**`setSigningKey()`必须使用生成Token时的相同密钥,否则解析失败[^2]。2.**自动过期检查**`parseClaimsJws()`方法会自动验证`exp`字段,若Token过期则抛出`ExpiredJwtException`。3.**防并发处理**在标记`jti`为已使用时,需保证操作的原子性(如Redis的`SETkeyvalueEXsecondsNX`命令)[^1]。4.**错误处理**捕获以下常见异常:-`ExpiredJwtException`:Token过期-`MalformedJwtException`:Token格式错误-`SignatureException`:签名不匹配-自定义重复使用异常>提示:一次性Token解析流程与普通JWT相同,核心差异在于**增加了jti使用状态的检查**。---###Python示例(使用`PyJWT`库)```pythonimportjwtimporttimeSECRET_KEY="your-strong-secret-key"defvalidate_one_time_token(token):try:#1.解析Token(自动验证签名和有效期)payload=jwt.decode(token,SECRET_KEY,algorithms=["HS256"])#2.获取jtijti=payload['jti']#3.检查是否已使用(示例:Redis)ifredis.exists(jti):#伪代码raiseException("Token使用")#4.标记为已使用(设置7分钟过期)redis.setex(jti,420,"used")#7*60=420秒returnTrueexceptjwt.ExpiredSignatureError:print("Token已过期")exceptjwt.InvalidTokenError:print("无效Token")exceptExceptionase:print(str(e))returnFalse```---###解析流程示意图```mermaidsequenceDiagramparticipantClientparticipantServerparticipantRedisClient->>Server:携带Token的请求Server->>Server:解析Token(验证签名/有效期)Server->>Redis:查询jti是否存在altjti不存在Redis-->>Server:返回falseServer->>Redis:存储jti(设置过期时间)Server-->>Client:返回成功elsejti已存在Redis-->>Server:返回trueServer-->>Client:返回"Token使用"end```通过以上步骤,即可安全解析并验证一次性Token的有效性[^1][^2]。§§相关问题§§1.如何在高并发场景下保证一次性Token验证的原子性?2.JWTToken解析失败有哪些常见原因?3.如何测试一次性Token解析功能的正确性?</think>### 使用 JWT 解析一次性 Token 的方法与示例代码 解析一次性 Token 的核心步骤与普通 JWT Token 一致,但需额外验证其唯一性和使用状态。以下是详细方法和代码实现(基于 Java jjwt 库): --- #### **解析步骤** 1. **获取并验证 Token** - 从 HTTP 请求头获取 Token(如 `Authorization: Bearer <token>`) - 验证签名有效性(防止篡改) - 自动检查过期时间(`exp`字段) 2. **提取关键信息** - 获取载荷(Payload)中的声明(Claims) - 重点提取唯一标识符 `jti` 3. **验证一次性** - 检查该 `jti` 是否已被使用过(查询缓存/数据库) - 标记为已使用(防止重复使用) --- #### **Java 示例代码(使用 jjwt 库)** ```java import io.jsonwebtoken.Claims; import io.jsonwebtoken.Jws; import io.jsonwebtoken.Jwts; import io.jsonwebtoken.security.Keys; import javax.crypto.SecretKey; public class JwtParser { // 密钥(需与生成Token时一致) private static final String SECRET = "your-256-bit-secret-key-here-1234567890"; private static final SecretKey SECRET_KEY = Keys.hmacShaKeyFor(SECRET.getBytes()); public Claims parseOneTimeToken(String token) { try { // 1. 解析并验证Token Jws<Claims> claimsJws = Jwts.parserBuilder() .setSigningKey(SECRET_KEY) .build() .parseClaimsJws(token); Claims claims = claimsJws.getBody(); String jti = claims.getId(); // 获取唯一标识符 // 2. 检查是否已使用(伪代码) if (isTokenUsed(jti)) { throw new SecurityException("Token已被使用"); } // 3. 标记为已使用(伪代码) markTokenUsed(jti); return claims; // 返回解析后的数据 } catch (Exception e) { // 处理各类异常: // ExpiredJwtException(过期) // UnsupportedJwtException(格式错误) // SignatureException(签名无效) throw new SecurityException("Token验证失败: " + e.getMessage()); } } // 实际需实现存储逻辑(如Redis/Database) private boolean isTokenUsed(String jti) { // 查询缓存/数据库是否存在该jti // return redis.exists(jti); return false; } private void markTokenUsed(String jti) { // 存储jti并设置过期时间(略长于Token有效期) // redis.setex(jti, 10*60, "used"); // 10分钟过期 } } ``` --- #### **关键解析说明** 1. **签名验证** 通过 `setSigningKey()` 使用与生成 Token 时相同的密钥验证签名,确保 Token 未被篡改[^2]。 2. **自动过期检查** `parseClaimsJws()` 会自动验证 `exp` 字段,若过期则抛出 `ExpiredJwtException`[^1]。 3. **一次性验证核心** ```java String jti = claims.getId(); // 从Payload获取jti if (isTokenUsed(jti)) { ... } // 关键检查 ``` 通过验证 `jti` 是否已被记录,确保 Token使用一次。 4. **错误处理** 需捕获的异常类型: - `ExpiredJwtException`:Token 过期 - `MalformedJwtException`:Token 结构错误 - `SignatureException`:签名验证失败 - `IllegalArgumentException`:非法参数 --- #### **Python 示例(PyJWT 库)** ```python import jwt from jwt.exceptions import ExpiredSignatureError, InvalidTokenError SECRET_KEY = "your-strong-secret-key" def parse_one_time_token(token): try: # 解析并验证Token payload = jwt.decode( token, SECRET_KEY, algorithms=["HS256"] ) # 检查jti是否已使用 if redis.exists(payload['jti']): # 伪代码 raise ValueError("Token already used") # 标记为已使用 redis.setex(payload['jti'], 600, "used") # 10分钟过期 return payload except ExpiredSignatureError: print("Token expired") except InvalidTokenError: print("Invalid token") ``` --- #### **解析结果示例** 成功解析后的 Claims 对象包含: ```json { "jti": "a1b2c3d4-e5f6-7890", // 唯一标识 "sub": "user123", // 用户标识 "iat": 1715000000, // 签发时间 "exp": 1715000300 // 过期时间(5分钟后) } ``` > **安全提示**: > 1. 密钥存储:使用安全机制管理密钥(如 KMS) > 2. HTTPS:必须在 TLS 加密通道中传输 Token > 3. 短时效:一次性 Token 有效期建议 ≤5 分钟[^1] 通过以上步骤,即可安全解析并验证一次性 Token 的有效性和唯一性[^1][^2]。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值