#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
, но я также следую нескольким другим примерам библиотек аутентификации токенов Django4. Разве simplejwt не справляется с этим изящно? Это есть в вашем списке примеров?