У меня есть приложение 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? Или, может быть, мне нужно переписать некоторые классы разрешений? Заранее спасибо.