python - (MissingClaimError) 使用 Praetorian 生成 JWT 令牌的 Python 烧瓶!str(uuid.uuid4()) 返回 null
问题描述
我正在执行一项检查由 Python Praetorian 创建的 JWT 令牌的有效性的任务。我使用 encode_jwt_token() (来自 Praetorian)在用户登录时生成令牌(定义如下):
def encode_jwt_token(
self, user,
override_access_lifespan=None, override_refresh_lifespan=None,
bypass_user_check=False, is_registration_token=False,
is_reset_token=False,
**custom_claims
):
"""
Encodes user data into a jwt token that can be used for authorization
at protected endpoints
:param: override_access_lifespan: Override's the instance's access
lifespan to set a custom duration
after which the new token's
accessability will expire. May not
exceed the refresh_lifespan
:param: override_refresh_lifespan: Override's the instance's refresh
lifespan to set a custom duration
after which the new token's
refreshability will expire.
:param: bypass_user_check: Override checking the user for
being real/active. Used for
registration token generation.
:param: is_registration_token: Indicates that the token will be
used only for email-based
registration
:param: custom_claims: Additional claims that should
be packed in the payload. Note that
any claims supplied here must be
JSON compatible types
"""
ClaimCollisionError.require_condition(
set(custom_claims.keys()).isdisjoint(RESERVED_CLAIMS),
"The custom claims collide with required claims",
)
if not bypass_user_check:
self._check_user(user)
moment = pendulum.now('UTC')
if override_refresh_lifespan is None:
refresh_lifespan = self.refresh_lifespan
else:
refresh_lifespan = override_refresh_lifespan
refresh_expiration = (moment + refresh_lifespan).int_timestamp
if override_access_lifespan is None:
access_lifespan = self.access_lifespan
else:
access_lifespan = override_access_lifespan
access_expiration = min(
(moment + access_lifespan).int_timestamp,
refresh_expiration,
)
payload_parts = {
'iat': moment.int_timestamp,
'exp': access_expiration,
'jti': str(uuid.uuid4()),
'id': user.identity,
'rls': ','.join(user.rolenames),
REFRESH_EXPIRATION_CLAIM: refresh_expiration,
}
if is_registration_token:
payload_parts[IS_REGISTRATION_TOKEN_CLAIM] = True
if is_reset_token:
payload_parts[IS_RESET_TOKEN_CLAIM] = True
flask.current_app.logger.debug(
"Attaching custom claims: {}".format(custom_claims),
)
payload_parts.update(custom_claims)
if self.encode_jwt_token_hook:
self.encode_jwt_token_hook(**payload_parts)
return jwt.encode(
payload_parts, self.encode_key, self.encode_algorithm,
)
为了检查令牌是否过期,我调用 _validate_jwt_data() (来自 Praetorian)来检查是否抛出了 ExpiredAccessError。问题是我总是遇到 MissingClaimError(Token is missing jti claim)。这是验证的定义:
def _validate_jwt_data(self, data, access_type):
"""
Validates that the data for a jwt token is valid
"""
MissingClaimError.require_condition(
'jti' in data,
'Token is missing jti claim',
)
BlacklistedError.require_condition(
not self.is_blacklisted(data['jti']),
'Token has a blacklisted jti',
)
MissingClaimError.require_condition(
'id' in data,
'Token is missing id field',
)
MissingClaimError.require_condition(
'exp' in data,
'Token is missing exp claim',
)
MissingClaimError.require_condition(
REFRESH_EXPIRATION_CLAIM in data,
'Token is missing {} claim'.format(REFRESH_EXPIRATION_CLAIM),
)
moment = pendulum.now('UTC').int_timestamp
if access_type == AccessType.access:
MisusedRegistrationToken.require_condition(
IS_REGISTRATION_TOKEN_CLAIM not in data,
"registration token used for access"
)
MisusedResetToken.require_condition(
IS_RESET_TOKEN_CLAIM not in data,
"password reset token used for access"
)
ExpiredAccessError.require_condition(
moment <= data['exp'],
'access permission has expired',
)
elif access_type == AccessType.refresh:
MisusedRegistrationToken.require_condition(
IS_REGISTRATION_TOKEN_CLAIM not in data,
"registration token used for refresh"
)
MisusedResetToken.require_condition(
IS_RESET_TOKEN_CLAIM not in data,
"password reset token used for refresh"
)
EarlyRefreshError.require_condition(
moment > data['exp'],
'access permission for token has not expired. may not refresh',
)
ExpiredRefreshError.require_condition(
moment <= data[REFRESH_EXPIRATION_CLAIM],
'refresh permission for token has expired',
)
elif access_type == AccessType.register:
ExpiredAccessError.require_condition(
moment <= data['exp'],
'register permission has expired',
)
InvalidRegistrationToken.require_condition(
IS_REGISTRATION_TOKEN_CLAIM in data,
"invalid registration token used for verification"
)
MisusedResetToken.require_condition(
IS_RESET_TOKEN_CLAIM not in data,
"password reset token used for registration"
)
elif access_type == AccessType.reset:
MisusedRegistrationToken.require_condition(
IS_REGISTRATION_TOKEN_CLAIM not in data,
"registration token used for reset"
)
ExpiredAccessError.require_condition(
moment <= data['exp'],
'reset permission has expired',
)
InvalidResetToken.require_condition(
IS_RESET_TOKEN_CLAIM in data,
"invalid reset token used for verification"
)
在查看了 encode_jwt_token() 的工作原理后,我看到使用了“jti : str(uuid.uuid4())”。这将为我的令牌创建一个唯一 ID。
总结一下: encode_jwt_token() 在我创建令牌时自动设置“jti”。它使用 str(uuid.uuid4()) 返回“jti: null”。看来我无法从 Praetorian 更改此定义的功能。
我需要: 找到一种方法,使“jti”被签署一个有效值。或者弄清楚为什么 str(uuid.uuid4()) 返回 null。
问题: 我认为忽略我的令牌上的这个空 ID 不是最好的主意。是这样吗?为什么?
这是我调用来检查令牌并在需要时刷新它的函数。它使用from ..extensions import guard
:
@auth_blueprint.route("/refresh", methods=['POST'])
def refresh():
data = request.json
print(data)
curent_token = data['token']
try:
guard._validate_jwt_data(curent_token, access_type= AccessType.access )
except MissingClaimError as e:
ret = {'token' : curent_token, 'status' : 'MissingClaimError'}
print(e)
except ExpiredAccessError:
print('This is the expired token access: ' + curent_token )
print('We caught an error ExpiredAcessError! ')
print("Begin refresh request")
try:
new_token = guard.refresh_jwt_token(curent_token)
print("Token was refreshed!")
ret = {'token': new_token , 'status': 'Refreshed access'}
except ExpiredRefreshError:
print("ExpiredRefreshError, request login!")
ret = {'token' : 'LogInYouDonkey' , 'status': 'could not refresh '}
return ret, 403
return ret, 200
解决方案
推荐阅读
- java - Room SQLite - 如何使 SELECT 返回 1 个对象?
- r - 根据 R 中另一个矩阵给出的位置更改一个矩阵中的元素
- java - 如何获取同时包含 JSON 对象和数组的 JSON 数据
- selenium - 通过 Selenium 与 IE 11 一起使用的正确 IEDriverServer 版本是什么
- c# - C# 如何提高 Direct2D 绘图的效率
- d3.js - 使用 nvd3 创建分组堆叠条形图
- python - Google Cloud Functions - Python - 没有函数名称的 HTTP 触发器 URL
- r - 从嵌套小标题的一个图形上的两个数据列表中绘图
- javascript - 从'react'导入*作为React;vs 从 'react' 导入 React;
- jenkins-x - “预览”入口服务配置