API-интерфейс Django Rest Framework вызывается дважды

#django #django-rest-framework #django-views

#джанго #django-rest-фреймворк #джанго-просмотры

Вопрос:

Я использую платформу Django Rest для создания пользовательского бэкенда, который выполняет вход в систему web3. Тем не менее, он дважды вызывает мою функцию аутентификации. И я не могу понять, почему.

мой символический взгляд:

 #expects public_address, nonce and token if user is currently signed in @api_view(["POST"]) def get_token(request):  logger.debug(request.data)  public_address = request.data["public_address"]  web3 = Web3Backend()  logger.debug(web3)  logger.debug('running authenticate from token endpoint')  user, token = web3.authenticate(request)  logger.debug(user)  logger.debug(token)  if token:  return JsonResponse({'token': token})  else:  return Response({'message': 'Missing token'}, status=400)  

Функция Аутентификации:

 def authenticate(self, request, **kwargs):  logger.debug(request);  public_address = request.data.get('public_address', None)  nonce = request.data.get('nonce', None)  curr_token = request.data.get('token', None)  Web3User = get_user_model()  if public_address:  if curr_token:  #TODO: decode token and check if public_address is the same as the user calling it and if not expired  #TODO: if yes then just return true and token  token =jwt.decode(curr_token, SECRET_KEY, algorithms="HS256")  #TODO: convert into datetime and make sure the current datetime is not pass this  expiry = datetime. strptime(token['expiry'],'%y-%m-%d')  now = datetime.date.today()  logger.debug(expiry)  logger.debug(now)  if(token['user'] == public_address and expiry lt; now):  logger.debug('JWT still valid')  return True, curr_token  else:  return AuthenticationFailed()  #TODO: decode the JWT and check if the user is the proper user  try:  #TODO: database check; will want to switch to JWT tokens in the future with refresh check to grab user  logger.debug('grabbing web3user to authenticate')  web3user = Web3User.objects.get(public_address=public_address)  #TODO: check nonce is signed correctly by user's private key by using the public key  if (web3user and self._check_nonce(web3user, nonce)):  logger.debug('everything passed')  #TODO: make JWT expiration in Django server config  expiry = datetime.date.today()   datetime.timedelta(days=1)  token = jwt.encode({"user": public_address, "expiry": str(expiry) }, SECRET_KEY, algorithm="HS256")  try:  web3user.nonce = uuid.uuid4().hex  web3user.save()  except Ex:  #TODO: return an exception response  logger.debug(Ex)  #TODO: 500 error  return NotFound()  logger.debug(web3user)  logger.debug(token)  return web3user, token  else:  #TODO: return a 401 unauthorized  logger.debug('nonce not correct')  pass  except Web3User.DoesNotExist:  #TODO: return an exception response  logger.debug('user not found')  return Web3User.DoesNotExist  else:  #TODO: return a 204 to signify a user does not exist and have the frontend to send a POST /user request  logger.debug('no nonce or public_address')  return None, None  

Мой вывод, но я стерла свой адрес:

 Django version 3.2.8, using settings 'Dapp.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C. [02/Dec/2021 19:47:05] "GET /api/user/0x50b67302a301d9489b998fa388c2398df9b8c2fb HTTP/1.1" 200 139 lt;rest_framework.request.Request: POST '/api/token/'gt; grabbing web3user to authenticate 0xMY_ADDRESSS 0xMY_ADDRESSS True everything passed 0xMY_ADDRESSS eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyIjoiMHg1MGI2NzMwMmEzMDFkOTQ4OWI5OThmYTM4OGMyMzk4ZGY5YjhjMmZiIiwiZXhwaXJ5IjoiMjAyMS0xMi0wMyJ9.vfdAktrnSBvhwc11_kkjhX_-Yr7YE8G5dXG0lEfAO0g {'nonce': '0xd315b6d63a0eef332cdb56b7fdb6e22b3ee238e742df5399ea613b8df681c4cf35f480583ccc8008a803a91418d3155681274ed089b40e6fa02b5ee00de1ccfe1c', 'public_address': '0x50b67302a301d9489b998fa388c2398df9b8c2fb'} lt;core.auth.backends.Web3Backend object at 0x7fca6d161760gt; running authenticate from token endpoint lt;rest_framework.request.Request: POST '/api/token/'gt; grabbing web3user to authenticate 0x8598f086842e4486cb35bd4b109054a066f27187 0x50b67302a301d9489b998fa388c2398df9b8c2fb False nonce not correct Internal Server Error: /api/token/ Traceback (most recent call last):  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner  response = get_response(request)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response  response = wrapped_callback(request, *callback_args, **callback_kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view  return view_func(*args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view  return self.dispatch(request, *args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch  response = self.handle_exception(exc)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception  self.raise_uncaught_exception(exc)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception  raise exc  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch  response = handler(request, *args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/decorators.py", line 50, in handler  return func(*args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/core/auth/views.py", line 25, in get_token  user, token = web3.authenticate(request) TypeError: cannot unpack non-iterable NoneType object Internal Server Error: /api/token/ Traceback (most recent call last):  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/core/handlers/exception.py", line 47, in inner  response = get_response(request)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/core/handlers/base.py", line 181, in _get_response  response = wrapped_callback(request, *callback_args, **callback_kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view  return view_func(*args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/django/views/generic/base.py", line 70, in view  return self.dispatch(request, *args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 509, in dispatch  response = self.handle_exception(exc)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 469, in handle_exception  self.raise_uncaught_exception(exc)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception  raise exc  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/views.py", line 506, in dispatch  response = handler(request, *args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/env/lib/python3.8/site-packages/rest_framework/decorators.py", line 50, in handler  return func(*args, **kwargs)  File "/Users/kcelica/Code/Django-for-web3/core/auth/views.py", line 25, in get_token  user, token = web3.authenticate(request) TypeError: cannot unpack non-iterable NoneType object  

Доказательство того, что мой интерфейс вызывает конечную точку /token только один раз:

введите описание изображения здесь

Настройки Django:

 INSTALLED_APPS = [  'django.contrib.auth',  'django.contrib.contenttypes',  'django.contrib.sessions',  'django.contrib.messages',  'django.contrib.staticfiles',  'rest_framework',  'core',  'django.contrib.admin', ]  AUTH_USER_MODEL='core.Web3User'  AUTHENTICATION_BACKENDS = [  'django.contrib.auth.backends.ModelBackend',  'core.auth.backends.Web3Backend'  ]   MIDDLEWARE = [  'django.middleware.security.SecurityMiddleware',  'django.contrib.sessions.middleware.SessionMiddleware',  'corsheaders.middleware.CorsMiddleware',  'django.middleware.common.CommonMiddleware',  'django.middleware.csrf.CsrfViewMiddleware',  'django.contrib.auth.middleware.AuthenticationMiddleware',  'django.contrib.messages.middleware.MessageMiddleware',  'django.middleware.clickjacking.XFrameOptionsMiddleware', ]  ROOT_URLCONF = 'Dapp.urls'  TEMPLATES = [  {  'BACKEND': 'django.template.backends.django.DjangoTemplates',  'DIRS': [],  'APP_DIRS': True,  'OPTIONS': {  'context_processors': [  'django.template.context_processors.debug',  'django.template.context_processors.request',  'django.contrib.auth.context_processors.auth',  'django.contrib.messages.context_processors.messages',  ],  },  }, ]  WSGI_APPLICATION = 'Dapp.wsgi.application'  

Комментарии:

1. Я не уверен, что ваш интерфейс не вызывает этот API токенов дважды.

2. Я проверил, добавив вызов в свой клиент прямо перед тем, как он сделает запрос.

3. @KeoniGarner опубликовал фотографию моей вкладки в сети, чтобы показать, что она вызывает ее только один раз.

4. Таким образом, представление токена не вызывается дважды, это просто функция аутентификации?

5. @KeoniGarner добавил это в пост!

Ответ №1:

Решил эту проблему, просто переписав ее как представление на основе классов под названием Web3UserToken и создав метод post. Однако я не знаю, почему это исправлено, поэтому оставлю это до тех пор, пока у меня не будет объяснения, прежде чем дать полный ответ.

Я думаю, однако, что, возможно, версия декоратора вызывает аутентификацию внизу, и поэтому, когда я вызывал /api/токен/, он вызывал аутентификацию, а затем снова аутентифицировался, на мой взгляд.

Комментарии:

1. Итак, ваша догадка оказалась верной — декоратор вызывает ваш метод аутентификации. Способ работы декоратора заключается в том, что он в основном создает представление на основе классов со всеми классами по умолчанию, включая классы аутентификации. Аутентификация в DRF работает путем проверки ваших настроенных бэкендов аутентификации до тех пор, пока не будет найдено совпадение, но она проверяет только при обращении к request.user. Ваше представление на основе классов позволяет обойти это, установив классы аутентификации в пустой список (что вы также можете сделать с помощью декоратора, но для этого будет использоваться отдельный декоратор.

2. Лично я думаю, что вам следует прекратить напрямую звонить своему Web3Backend и использовать аутентификацию DRF, но это ваша прерогатива.

3. верно, но я как бы застрял на шаблоне дизайна, чтобы вернуть токен. и я не хочу хранить свой токен в БД — если бы я это сделал, я мог бы просто вернуться user.token , но я также следую нескольким другим примерам библиотек аутентификации токенов Django

4. Разве simplejwt не справляется с этим изящно? Это есть в вашем списке примеров?