将经过 REST 化的 Django 的 JWT 认证完全交给 dj_rest_auth
到目前为止的经过
-
- 在本地搭建了Ubuntu Server,
-
- 安装了Docker Compose,
-
- 使用Docker Compose构建了Django+MySQL+nginx的开发环境,
-
- 尝试在AWS Fargate上运行Django,但失败了。
-
- 通过继承AbstractBaseUser创建了一个自定义用户,可以通过电子邮件进行登录。
- 通过使用django-environ和环境变量设置,将在Git上公开的敏感变量隐藏起来。
以下是 GitHub 存储库链接:https://github.com/hajime-f/octave。
这次想要做的事情
我們從上上次開始,當時我們使得自訂用戶能夠透過電子郵件登錄。
我想要做的两件事是:
– 引入 Django REST Framework
– 使用 dj_rest_auth 实现 JWT 认证。
如果有人说「REST是什么东西」,请阅读这篇文章。我从零开始研究了一下REST API。
如果有人问「JWT(ジョット)认证是什么?」,以下内容可能会有所帮助。
关于JWT认证和流程的简明解释。
步骤
-
- 在运行Django的容器中安装包
-
- 编辑settings.py文件
-
- 在urls.py中设置路由
- 将静态文件收集到基础目录中
安装软件包
首先,安装 REST Framework 和 JWT认证的软件包。要在容器中安装这些软件包,需要编辑requirements.txt文件。请注意,路径是从docker-compose.dev.yml所在的项目目录开始计算的相对路径。
Django==3.1.4
uwsgi==2.0.18
mysqlclient==1.4.6
django-environ==0.4.5
djangorestframework==3.12.2 # 追加
djangorestframework-simplejwt==4.6.0 # 追加
dj-rest-auth==2.1.2 # 追加
完成编辑后,请进行构建。
$ make dev # docker-compose -f docker-compose.dev.yml build と同じ
请读取前面的文章,以获取Makefile的完整内容。
修改 settings.py 文件。
按照公式文档的指示,编辑settings.py文件。
INSTALLED_APPS = [
・・・
'django.contrib.sites', # 追加
# 3rd party apps
'rest_framework', # 追加
'rest_framework.authtoken', # 追加
'dj_rest_auth', # 追加
#My applications
'users',
]
# 以下すべて追加
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'dj_rest_auth.jwt_auth.JWTCookieAuthentication',
)
}
SITE_ID = 1
REST_USE_JWT = True
JWT_AUTH_COOKIE = 'user'
AUTH_USER_MODEL = 'users.User'
SIMPLE_JWT = {
'USER_ID_FIELD': 'uuid',
}
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
将uuid作为用于标识用户的主键,因此需要进行相应的设置。
此外,如前文所述,为了实现仅通过电子邮件地址(不使用用户名)进行认证,系统中包含了多项相关设置。
在 urls.py 中设置路由
按照官方文档所述,在urls.py中进行路由设置。
from django.contrib import admin
from django.urls import path
from django.urls import include # 追加
urlpatterns = [
path('admin/', admin.site.urls),
path('dj-rest-auth/', include('dj_rest_auth.urls')), # 追加
]
4. 将静态文件收集到基础目录中
由于REST框架具有自己的静态文件,因此让我们将这些静态文件收集到基础目录中。
$ docker-compose -f docker-compose.dev.yml run python ./manage.py collectstatic
确认动作
请用浏览器访问 http://[开发环境的IP地址]:[开发环境的端口号]/dj-rest-auth/login/,并确保可以使用测试用户登录。
如果以这种方式返回访问令牌,那就意味着成功。
即使从终端执行curl命令,也可以查看结果。
$ curl \
-X POST \
-H "Content-Type: application/json" \
-d '{"email": "test@test.com", "password": "test123456"}' \
http://[開発環境のIPアドレス]:[開発環境のポート番号]/dj-rest-auth/login/
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGci...",
"user":{"pk":"de0e84a7-8396-497e-aa07-e143cc0a2811",
"email":"test@test.com",
"first_name":"テスト",
"last_name":"テスト"}}%
现在正在遭受困扰的错误
(补充)当我向官方提问后,这个错误得到了解决。
实际上,只要这样做,用户注册也应该可以完全交给别人做的。
Django==3.1.4
uwsgi==2.0.18
mysqlclient==1.4.6
django-environ==0.4.5
djangorestframework==3.12.2
djangorestframework-simplejwt==4.6.0
dj-rest-auth==2.1.2
django-allauth==0.44.0 # さらに追加
INSTALLED_APPS = [
・・・
'django.contrib.sites',
# 3rd party apps
'rest_framework',
'rest_framework.authtoken',
'allauth', # さらに追加
'allauth.account', # さらに追加
'dj_rest_auth',
'dj_rest_auth.registration', # さらに追加
#My applications
'users',
]
・・・
ACCOUNT_AUTHENTICATION_METHOD = 'email'
ACCOUNT_USERNAME_REQUIRED = False
ACCOUNT_USER_MODEL_USERNAME_FIELD = None
ACCOUNT_EMAIL_REQUIRED = True # さらに追加
ACCOUNT_UNIQUE_EMAIL = True # さらに追加
ACCOUNT_EMAIL_VERIFICATION = 'mandatory' # さらに追加
urlpatterns = [
path('admin/', admin.site.urls),
path('dj-rest-auth/', include('dj_rest_auth.urls')),
path('dj-rest-auth/registration/', include('dj_rest_auth.registration.urls')), # さらに追加
]
这是根据官方文件的说法,但由于使用了自定义用户,所以出现了”EmailAddress matching query does not exist”的异常,甚至登录功能都无法正常工作。
我正在烦恼中。为什么呢。
我贴上以下的错误日志,请知道的人告诉我。
Environment:
Request Method: POST
Request URL: http://127.0.0.1:8081/dj-rest-auth/login/
Django Version: 3.1.4
Python Version: 3.9.1
Installed Applications:
['django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'django.contrib.sites',
'rest_framework',
'rest_framework.authtoken',
'allauth',
'allauth.account',
'allauth.socialaccount',
'dj_rest_auth',
'dj_rest_auth.registration',
'users',
'orchestra']
Installed Middleware:
['django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware']
Traceback (most recent call last):
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/exception.py", line 47, in inner
response = get_response(request)
File "/usr/local/lib/python3.9/site-packages/django/core/handlers/base.py", line 179, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/decorators/csrf.py", line 54, in wrapped_view
return view_func(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/generic/base.py", line 70, in view
return self.dispatch(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/utils/decorators.py", line 43, in _wrapper
return bound_method(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/views/decorators/debug.py", line 89, in sensitive_post_parameters_wrapper
return view(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/dj_rest_auth/views.py", line 48, in dispatch
return super(LoginView, self).dispatch(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 509, in dispatch
response = self.handle_exception(exc)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 469, in handle_exception
self.raise_uncaught_exception(exc)
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 480, in raise_uncaught_exception
raise exc
File "/usr/local/lib/python3.9/site-packages/rest_framework/views.py", line 506, in dispatch
response = handler(request, *args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/dj_rest_auth/views.py", line 138, in post
self.serializer.is_valid(raise_exception=True)
File "/usr/local/lib/python3.9/site-packages/rest_framework/serializers.py", line 220, in is_valid
self._validated_data = self.run_validation(self.initial_data)
File "/usr/local/lib/python3.9/site-packages/rest_framework/serializers.py", line 422, in run_validation
value = self.validate(value)
File "/usr/local/lib/python3.9/site-packages/dj_rest_auth/serializers.py", line 131, in validate
self.validate_email_verification_status(user)
File "/usr/local/lib/python3.9/site-packages/dj_rest_auth/serializers.py", line 112, in validate_email_verification_status
email_address = user.emailaddress_set.get(email=user.email)
File "/usr/local/lib/python3.9/site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "/usr/local/lib/python3.9/site-packages/django/db/models/query.py", line 429, in get
raise self.model.DoesNotExist(
Exception Type: DoesNotExist at /dj-rest-auth/login/
Exception Value: EmailAddress matching query does not exist.
请参考
-
- dj-rest-auth の公式ドキュメント
- simple JWT の公式ドキュメント