当前位置: 首页 > 科技观察

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

时间:2023-03-14 14:58:34 科技观察

当初在DRF(DjangoRESTFramework)中使用JWT的时候,就觉得JWT很神奇。它不使用session、cookie、数据库,仅靠一段加密字符串解决了用户认证的麻烦。直到遇到一个当时百思不得其解的问题,才揭开了它的神秘面纱。当时遇到的问题是无论怎么设置JWTTOKEN的过期时间,都没有生效。即使设置为1秒后过期,1分钟后TOKEN仍然可以正常使用,即使重启了Django服务。没办法,只好硬着头皮追源码,看看JWT是怎么判断TOKEN是否过期的。具体方法是先深入JWT代码溯源。DRF中DEFAULT_AUTHENTICATION_CLASSES的配置是JWT:直接定位到这个类,发现它继承自BaseJSONWebTOKENAuthentication再查看BaseJSONWebTOKENAuthentication,发现有一个判断过期的逻辑:继续展开jwt_decode_handler函数,发现调用用于扩展jwt的jwt.decode函数。decode函数,发现调用了函数_validate_claims,函数_validate_claims调用了_validate_exp,然后对_validate_exp展开,发现这段:发现过期时间exp来自payload,payload来自TOKEN本身:目前答案是透露出来,原来TOKEN的过期时间其实是编码在TOKEN本身的。服务器收到TOKEN后,先解码,解码过期时间,再与当前时间进行比较。如果当前时间比较小,说明还没有过期,TOKEN是有效的。否则,它将返回给客户。终端“签名已过期”。我调试了这个TOKEN的过期时间exp,发现这个exp是修改JWT_EXPIRATION_DELTA之前的过期时间。原来修改JWT_EXPIRATION_DELTA后需要重新生成TOKEN,这样过期时间就会以新的为准。至此,JWT的原理就很清楚了:当用户第一次登录时,服务端(JWT)会获取用户名和用户id,并构建设置过期时间的payload:payload={'user_id':user.pk,'username':username,'exp':datetime.utcnow()+api_settings.JWT_EXPIRATION_DELTA}然后用设定的算法加密payload,用私钥tokendefjwt_encode_handler(payload):key=api_settings.服务器在收到请求时首先验证令牌。验证过程是对token进行反向解码:defjwt_decode_handler(token):options={'verify_exp':api_settings.JWT_VERIFY_EXPIRATION,}#getuserfromtoken,BEFOREverification,togetusersecretkeyunverified_pa??yload=jwt.decode(token,None,False)secret_key=jwt_get_secret_key(unverified_pa??yload)returnjwt.decode(token,api_settings.JWT_PUBLIC_KEYorsecret_key,api_settings.JWT_VERIFY,options=options,leeway=api_settings.JWT_LEEWAY,audience=api_settings.JWT_AUDIENCE,issuer=api_settings.JWT_ISSUER,algorithms=[api_settings.JWT_ALGORITHM])使用相同的算法进行解密,使用公钥或私钥进行解密,如果解密成功且没有过期,则认为用户有访问权限,正常返回。最后一个问题至少花了我半个小时。如果你遇到这种情况,并且能瞬间明白原因,那么本文的目的就达到了。源代码下没有秘密。遇到问题,看源码可能不是解决问题最快的方法,但却是提升自己最快的方法。很多开源软件设计模式的应用是非常值得学习的,比如DRF的模块设计,通过mixin的组合实现灵活可扩展的APIView,通过子类传入相关类实现自定义功能。如何编写一个灵活可扩展、高内聚低耦合、符合开闭原则的程序,阅读开源代码是一种非常高效的学习方式。当然,这需要先对设计模式进行系统的学习,这样才能有一双雪亮的眼睛,否则就是在守着金山而不自知。本文转载自微信公众号“Python7号”,可通过以下二维码关注。转载本文请联系Python七号公众号。