Перезапуск через N минут с Django и JWT - PullRequest
0 голосов
/ 26 октября 2018

Сценарий: Я хочу, чтобы пользователь повторно зарегистрировался при переходе в зону безопасности после N минут, например, когда пользователь собирается оплатить заказ, однако он вошел в систему 1 час назад, я хотел бы быть уверен, что это он. Это с помощью rest_framework_jwt.

Длинное описание:

Я недавно тестировал django для современной веб-разработки (так, бэкэнд с rest-api). Однако я столкнулся с проблемой, решение которой еще не нашел.

В rest_framework_jwt вы устанавливаете класс аутентификации следующим образом.

'DEFAULT_AUTHENTICATION_CLASSES': (
    'rest_framework_jwt.authentication.JSONWebTokenAuthentication',

Это сделает большую работу для общей цели. Однако я хочу, чтобы пользователь повторно идентифицировался (повторно входил в систему) при входе в область с разумной информацией через 10 минут после входа в систему, например, разумная информация может быть платёжной зоной. Таким образом, я хотел бы отправить параметр в класс Authentication, сообщающий, что пользователь находится в разумной области.

То, что я считаю возможным решением, но пока не знаю, как это сделать, заключается в следующем: rest_framework_jwt создает переменную orig_iat при использовании опции JWT_ALLOW_REFRESH. Я мог бы отправить флаг классу аутентификации, чтобы сказать, что текущее представление является чувствительной областью или нет, если это так, и пользователь вошел в систему более 10 минут назад, я могу отправить сообщение о том, что пользователю необходимо повторно войти в систему, чтобы продолжить .

Я не против развить проект rest_framework_jwt и адаптировать его для своих целей, однако я хотел бы знать, как отправить параметр из представления в класс аутентификации (в данном случае: rest_framework_jwt.authentication.JSONWebTokenAuthentication).

Кроме того, если для этого сценария уже что-то сделано с rest_framework_jwt, я бы хотел не изобретать колесо.

Ответы [ 3 ]

0 голосов
/ 31 октября 2018

В соответствии с инструкцией: https://getblimp.github.io/django-rest-framework-jwt/#additional-settings

Запрет обновления маркера должен выполнить эту работу.Дело в том, что вы получите только 1 токен и не сможете обновить его через 1 час.

JWT_AUTH = {
    'JWT_ALLOW_REFRESH': False,
    'JWT_REFRESH_EXPIRATION_DELTA': timedelta(hours=1),
}

Другие проблемы должны решаться на стороне интерфейса.Вы должны проверить, пытается ли пользователь перейти к «чувствительному» виду.Если да, то проверьте, является ли токен действительным.Если неверно - перенаправить на страницу входа.Если просмотр «нечувствителен» - ваш выбор.

0 голосов
/ 06 ноября 2018

Рекомендуемый способ справиться с этим - отделить свежесть токена от проверки токена.Для большинства представлений требуется действительный токен, для защищенных представлений требуется токен fresh , который не только действителен, но также выдан при входе в систему и с тех пор не обновлялся.

Вы можете сделать этоустановив флаг на токене, чтобы пометить его как «новый» при входе в систему, но сбросить флаг при обновлении.Затем поток становится следующим:

  1. Клиент получает доступ к сайту без токена -> Запретить доступ
  2. Клиент аутентифицируется с помощью конечной точки получения токена -> Токен выдачи с fresh=True
  3. Клиент (или сервер) обновляет действительный токен -> Токен выдачи с fresh=False
  4. Клиент получает доступ к незащищенной конечной точке с помощью действующего токена -> Токен принятия
  5. Клиент обращается к защищенной конечной точке -> Принятьтокен, только если для токена установлено 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,
    )
    # ...
0 голосов
/ 30 октября 2018

Ну ... Пока что я создал декоратор для представления функций. Код для декоратора выглядит следующим образом:

from functools import wraps
from rest_framework_jwt.settings import api_settings
from django.utils.translation import ugettext as _
from calendar import timegm
import datetime
jwt_decode_handler = api_settings.JWT_DECODE_HANDLER
def recently_authenticated():
    def decorator(func):
        @wraps(func)
        def inner(request, *args, **kwargs):
            jwt_payload = jwt_decode_handler(request._auth)
            rencent_auth_limit = api_settings.JWT_RECENT_AUTHENTICATION_DELTA
            if isinstance(rencent_auth_limit, datetime.timedelta):
                rencent_auth_limit = (rencent_auth_limit.days * 24 * 3600 +
                                 rencent_auth_limit.seconds) + jwt_payload["orig_iat"]
            timenow = timegm(datetime.datetime.utcnow().utctimetuple())
            if timenow>rencent_auth_limit:
                return Response({"detail":_("you have to reidentify to enter this area")},
                                status=401)
            return func(request, *args, **kwargs)
        return inner
    return decorator

Формат ответа задается в том же формате, что и rest_framework_jwt.authentication.JSONWebTokenAuthentication. Константа JWT_RECENT_AUTHENTICATION_DELTA - это специальный параметр, вставленный в settings.py пакета rest_framework_jwt (разветвление).

Наконец, чтобы использовать его, можно добавить декоратор к любому представлению. Например:

@api_view()
@recently_authenticated()
def index_view(request):
    data = User.objects.filter()
    return Response(UserSerializer(data, many=True).data)

А когда пользователь недавно прошел аутентификацию, он отправит сообщение {"detail":"you have to reidentify to enter this area"} с кодом 401. Это может быть оценено и проанализировано внешним интерфейсом и перенаправить пользователя на вход в систему.

Примечание: Декоратор оценивает только прошедшее время. rest_framework_jwt.authentication.JSONWebTokenAuthentication.

по-прежнему выполняет проверку, указывающую, является ли он правильным пользователем и правильным токеном.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...