JWT 的 Token 过期时间为什么没有生效

开发 前端
在我第一次在 DRF(Django REST Framework)中使用 JWT 时,感觉 JWT 非常神奇,它即没有使用 session、cookie,也不使用数据库,仅靠一段加密的字符串,就解决了用户身份验证的烦恼。

[[426216]]

在我第一次在 DRF(Django REST Framework)中使用 JWT 时,感觉 JWT 非常神奇,它即没有使用 session、cookie,也不使用数据库,仅靠一段加密的字符串,就解决了用户身份验证的烦恼。

直到我遇到了一个当时百思不得解的问题,才揭开了它的神秘面纱。

当时遇到的问题就是,无论怎么设置 JWT TOKEN 的过期时间,都没有生效,即使设置为 1 秒后过期,过了 1 分钟,TOKEN 还是可以正常使用,重启 Django 服务也不行。

没有别的办法,我就硬着头皮去追着源码,看看 JWT 是怎么判断 TOKEN 是否过期的。

具体的方法就是,深度优先追溯 JWT 代码的源头。在 DRF 中,配置了 DEFAULT_AUTHENTICATION_CLASSES 就是 JWT:

直接定位至这个类,发现它继承了 BaseJSONWebTOKENAuthentication

然后看 BaseJSONWebTOKENAuthentication,发现有一段判断过期的逻辑:

继续展开 jwt_decode_handler 这个函数,发现它调用了 jwt.decode 函数

展开 jwt.decode 函数,发现它调用了函数 _validate_claims

函数 _validate_claims 又调用了 _validate_exp,

然后展开 _validate_exp,找到了这段:

发现过期时间 exp 来自 payload,payload 又来自 TOKEN 本身:

至此谜底揭开,原来,TOKEN 的过期时间其实被编码在了 TOKEN 本身,服务器收到 TOKEN 时先进行解码,解码出过期时间,然后和当前时间进行对比,如果当前时间比较小,说明没有过期,TOKEN 就是有效的,否则返回客户端 "Signature has expired."

我 Debug 出了这个 TOKEN 的过期时间 exp,发现这个 exp 是修改 JWT_EXPIRATION_DELTA 之前的那个过期时间,原来修改 JWT_EXPIRATION_DELTA 之后需要重新生成 TOKEN,这样的过期时间才会按照新的来。

至此,JWT 的原理已经非常清晰了:

用户第一次登录时,服务器(JWT)会获得用户名、用户 id,在加上设置的过期时间构建 payload:

  1. payload = { 
  2.         'user_id'user.pk, 
  3.         'username': username, 
  4.         'exp': datetime.utcnow() + api_settings.JWT_EXPIRATION_DELTA 
  5.     } 

然后将 payload 用设置好的算法使用私钥加密成 token

  1. def jwt_encode_handler(payload): 
  2.     key = api_settings.JWT_PRIVATE_KEY or jwt_get_secret_key(payload) 
  3.     return jwt.encode( 
  4.         payload, 
  5.         key
  6.         api_settings.JWT_ALGORITHM 
  7.     ).decode('utf-8'

token 返回至客户端后,客户端缓存该 token,然后每一次请求时都带上该 token。

服务器在收到请求时先验证该 token,验证的过程就是对 token 进行逆向解码:

  1. def jwt_decode_handler(token): 
  2.     options = { 
  3.         'verify_exp': api_settings.JWT_VERIFY_EXPIRATION, 
  4.     } 
  5.     # get user from token, BEFORE verification, to get user secret key 
  6.     unverified_payload = jwt.decode(token, None, False
  7.     secret_key = jwt_get_secret_key(unverified_payload) 
  8.     return jwt.decode( 
  9.         token, 
  10.         api_settings.JWT_PUBLIC_KEY or secret_key, 
  11.         api_settings.JWT_VERIFY, 
  12.         options=options, 
  13.         leeway=api_settings.JWT_LEEWAY, 
  14.         audience=api_settings.JWT_AUDIENCE, 
  15.         issuer=api_settings.JWT_ISSUER, 
  16.         algorithms=[api_settings.JWT_ALGORITHM] 
  17.     ) 

解密使用同样的算法,使用公钥或私钥进行解密,解密成功且不过期,则认为用户有权限访问,正常返回。

最后

这个问题至少花了我半个小时的时间,如果你遇到这种情况,能瞬间明白其中缘由,那本文的目的就达到了。

源码之下无秘密,遇到问题,去看源码可能不是解决问题最快的方法,却是提升自己最快的方法。很多开源软件设计模式的应用都非常值得我们学习,比如 DRF 的模块设计,通过 mixins 组合来实现灵活可扩展的 APIView,通过子类传入相关的 class 来实现用户自定义的功能。如何写出灵活可扩展、高内聚低耦合、符合开闭原则的程序,阅读开源代码,是一个非常高效的学习方式。

当然了,这需要先对设计模式有一个系统的学习,让自己有一双慧眼,不然就是守着金山不自知。

本文转载自微信公众号「Python七号」,可以通过以下二维码关注。转载本文请联系Python七号公众号。

 

责任编辑:武晓燕 来源: Python七号
相关推荐

2018-07-19 14:01:23

数据库索引MySQL

2022-06-24 08:01:07

CSScontent元素

2024-02-21 21:28:29

Linux系统

2023-12-06 09:10:28

JWT微服务

2023-12-08 12:12:21

2022-06-12 21:36:57

Hooksreact

2024-09-12 08:32:42

2021-08-09 08:53:30

HTTP状态化协议

2021-05-19 09:37:45

SessionTokencookie

2020-07-22 07:55:12

Python开发函数

2009-12-04 13:31:21

PHP全局变量不能生效

2021-03-23 10:45:23

CookieSession前端

2020-10-27 09:00:00

NodeJS实现JWT

2020-10-20 07:49:00

JWT(JSON We

2022-09-05 08:01:20

JWTWeb安全

2015-04-23 10:15:53

AndroidiOS图片

2015-04-23 10:52:53

AndroidiOS图片

2021-07-21 09:35:36

switchbreakJava

2013-03-18 09:30:18

Lisp

2022-05-22 21:23:10

前端监控系统
点赞
收藏

51CTO技术栈公众号