Django REST Framework - переопределение JSONWebTokenAuthentication для пользовательской аутентификации JWT - PullRequest
0 голосов
/ 28 июня 2018

У меня есть приложение Django с внешней базой данных, что означает, что для каждого пользовательского запроса я отправляю запросы SQL на внешний сервер БД. Локальная БД (например, sqllite или такая) не существует. Кроме того, JWT должен использоваться для аутентификации пользователей. Для этого я переписал ObtainJSONWebToken просмотр:

class ObtainJWT(ObtainJSONWebToken):
    def post(self, request, *args, **kwargs):
        username = request.data.get('username')
        password = request.data.get('password')

        # verify that user with given credentials exist in db
        resp = requests.post(settings.SERVER_HOST+"/auth/",
                             json={"username":username, "password":password})
        if resp.status_code == status.HTTP_401_UNAUTHORIZED:
            return Response({'error':'Invalid credentials'},
                            status=status.HTTP_401_UNAUTHORIZED)

        # create token
        payload = jwtutils.jwt_payload_handler(username, password, api_settings.JWT_EXPIRATION_DELTA)
        token = jwt_encode_handler(payload)
        return Response({'token': token},
                        status=status.HTTP_200_OK)

А jwt_payload_handler в jwtutils:

def jwt_payload_handler(username, password, delta):
    # custom payload handler
    payload = {
        'username': username,
        'password': password,
        'exp': datetime.utcnow() + delta
    }

    return payload

Теперь я могу успешно получить токен без использования каких-либо User объектов. Но когда полученный токен используется (пользователь пытается получить доступ к защищенным маршрутам с помощью токена), возвращается {"detail":"Invalid signature."}. Я думаю, это потому, что у класса JSONWebTokenAuthentication, который я использую в DRF, есть метод authenticate_credentials, который проверяет, существует ли пользователь с данными учетными данными в локальной БД (https://github.com/GetBlimp/django-rest-framework-jwt/blob/master/rest_framework_jwt/authentication.py#L59), отсюда и ошибка. Поэтому я решил создать собственный класс аутентификации. Вот что я написал:

class JSONWebTokenAuthentication(BaseAuthentication):
"""
Token based authentication using the JSON Web Token standard.
"""
def get_jwt_value(self, request):
    auth = get_authorization_header(request).split()
    auth_header_prefix = api_settings.JWT_AUTH_HEADER_PREFIX.lower()

    if not auth:
        if api_settings.JWT_AUTH_COOKIE:
            return request.COOKIES.get(api_settings.JWT_AUTH_COOKIE)
        return None

    if smart_text(auth[0].lower()) != auth_header_prefix:
        return None

    if len(auth) == 1:
        msg = _('Invalid Authorization header. No credentials provided.')
        raise exceptions.AuthenticationFailed(msg)
    elif len(auth) > 2:
        msg = _('Invalid Authorization header. Credentials string '
                'should not contain spaces.')
        raise exceptions.AuthenticationFailed(msg)

    return auth[1]

def authenticate(self, request):
    """
    Returns a two-tuple of `User` and token if a valid signature has been
    supplied using JWT-based authentication.  Otherwise returns `None`.
    """
    jwt_value = self.get_jwt_value(request)
    if jwt_value is None:
        return None

    try:
        payload = jwt_decode_handler(jwt_value)
    except jwt.ExpiredSignature:
        msg = ('Signature has expired.')
        raise exceptions.AuthenticationFailed(msg)
    except jwt.DecodeError:
        msg = _('Error decoding signature.')
        raise exceptions.AuthenticationFailed(msg)
    except jwt.InvalidTokenError:
        raise exceptions.AuthenticationFailed()

    return (None, payload)

Однако это не работает. Я возвращаю None вместо действительного User объекта. Где-то позже в процессе аутентификации Django это значение читается и проверяется, если оно is_authenticated() возвращает True. Возвращение None, очевидно, приведет к {"detail":"You do not have permission to perform this action."}.

Я относительно новичок в Django и JWT, каков наилучший способ переписать класс аутентификации, чтобы я мог не сохранять какие-либо локальные файлы Django User и ничего не нарушать в процессе аутентификации Django? Или, может быть, мне нужно переписать некоторые классы разрешений? Заранее спасибо.

1 Ответ

0 голосов
/ 28 февраля 2019

Не уверен, что вы решили это, но добавили ли вы свой пользовательский класс аутентификации в настройках остальных фреймворков? как то так:

  REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'myapp.location.JSONWebTokenAuthentication' #location of your custom authentication class.
        ),
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...