Рекомендуемый способ справиться с этим - отделить свежесть токена от проверки токена.Для большинства представлений требуется действительный токен, для защищенных представлений требуется токен fresh , который не только действителен, но также выдан при входе в систему и с тех пор не обновлялся.
Вы можете сделать этоустановив флаг на токене, чтобы пометить его как «новый» при входе в систему, но сбросить флаг при обновлении.Затем поток становится следующим:
- Клиент получает доступ к сайту без токена -> Запретить доступ
- Клиент аутентифицируется с помощью конечной точки получения токена -> Токен выдачи с
fresh=True
- Клиент (или сервер) обновляет действительный токен -> Токен выдачи с
fresh=False
- Клиент получает доступ к незащищенной конечной точке с помощью действующего токена -> Токен принятия
- Клиент обращается к защищенной конечной точке -> Принятьтокен, только если для токена установлено
fresh=True
.
Единственный способ получить свежий токен - войти снова, обновление не разрешено.
Таким образом, вам нужночтобы:
- различать получение нового токена и обновление при создании полезной нагрузки JWT.
- проверять ключ
fresh
в текущем токене JWT, чтобы создать пользовательское разрешение .
Первое требование означает, что вам придется выполнить некоторое подклассирование представления, поскольку обратному вызову jwt_payload_handler
не предоставляется никакой информации для определения того, как он вызывается.
Самый простой способ справитьсяпервое требование состоит в том, чтобы просто подклассифицировать сериализаторы , используемые для производства свежего или обновленного токена , декодировать полученный токен, ввести соответствующее значение ключа fresh
, а затем перекодировать.Затем используйте подклассифицированные сериализаторы для создания нового набора представлений API :
from rest_framework_jwt.settings import api_settings
from rest_framework_jwt.serializers import JSONWebTokenSerializer, RefreshJSONWebTokenSerializer
from rest_framework_jwt.views import JSONWebTokenAPIView
jwt_encode_handler = api_settings.JWT_ENCODE_HANDLER
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
class FreshJSONWebTokenSerializer(JSONWebTokenSerializer):
"""Add a 'fresh=True' flag to the JWT token when issuing"""
def validate(self, *args, **kwargs):
result = super().validate(*args, **kwargs)
payload = jwt_decode_handler(result['token'])
return {
**result,
'token': jwt_encode_handler({**payload, fresh=True})
}
class NonFreshRefreshJSONWebTokenSerializer(RefreshJSONWebTokenSerializer):
"""Set the 'fresh' flag False on refresh"""
def validate(self, *args, **kwargs):
result = super().validate(*args, **kwargs)
payload = jwt_decode_handler(result['token'])
return {
**result,
'token': jwt_encode_handler({**payload, fresh=False})
}
class ObtainFreshJSONWebToken(JSONWebTokenAPIView):
serializer_class = JSONWebTokenSerializer
class NonFreshRefreshJSONWebToken(JSONWebTokenAPIView):
serializer_class = NonFreshRefreshJSONWebTokenSerializer
obtain_jwt_token = ObtainFreshJSONWebToken.as_view()
refresh_jwt_token = NonFreshRefreshJSONWebToken.as_view()
Затем зарегистрируйте эти два представления как конечные точки API вместо , предоставленные DjangoПроект REST Framework JWT, для получения и обновления путей.
Далее следует разрешение;поскольку класс JSONWebTokenAuthentication
возвращает декодированную полезную нагрузку при аутентификации атрибута request.auth
установлен в словарь полезной нагрузки, что позволяет нам проверять его непосредственно в настраиваемом разрешении:
class HashFreshTokenPermission(permissions.BasePermission):
message = 'This endpoint requires a fresh token, please obtain a new token.'
def has_permission(self, request, view):
return (
request.user and
request.user.is_authenticated and
request.auth and
isinstance(request.auth, dict) and
request.auth.get('fresh', False)
)
Зарегистрироватьсяэто разрешение с каркасом REST:
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
'yourmodule.HashFreshTokenPermission',
),
# ...
}
и используйте его в представлениях, чувствительных к безопасности:
class SampleSecureView(APIView):
permission_classes = (
permissions.IsAuthenticated,
HashFreshTokenPermission,
)
# ...