使用 SimpleJWT 登录时的 AnonymousUser
AnonymousUser when logged with SimpleJWT
我知道这不是第一个此类问题,但其他主题对我没有帮助。
我想使用 djangorestframework-simplejwt 对用户进行身份验证,一切都很好,因为它涉及到获取用户。我总是得到匿名用户。
我通过访问 api/token 并传递凭据来获取令牌。令牌创建正确,它包含正确的用户 ID。当我尝试访问受保护的视图时,它允许我访问,但 Django 无法识别用户。
settings.py
import os
from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ["SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"corsheaders",
"djmoney",
"bills",
"accounts",
]
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 = "shared_bills.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 = "shared_bills.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
# Authentication
AUTH_USER_MODEL = "accounts.User"
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = "/static/"
APPEND_SLASH = True
# CORS
CORS_ORIGIN_ALLOW_ALL = True
# Rest Framework
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTTokenUserAuthentication"
],
}
if DEBUG:
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
"rest_framework.renderers.BrowsableAPIRenderer"
)
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].extend(
[
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
]
)
# Simple JWT
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(weeks=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': os.environ["SECRET_KEY"],
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
views.py
"""Views for accounts application."""
from django.contrib.auth import get_user
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from accounts.models import User
from accounts.serializers import (
PasswordCheckSerializer,
UserChangePasswordSerializer,
UserSerializer,
)
class IsAnonymous(BasePermission):
"""Allows access only to anonymous users."""
def has_permission(self, request, view):
return not bool(request.user and request.user.is_authenticated)
class UserViewset(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
"""Viewset for User object."""
queryset = User.objects.all()
def get_serializer_class(self):
if self.action == "register":
return UserSerializer
if self.action == "change_password":
return UserChangePasswordSerializer
if self.action == "delete_user":
return PasswordCheckSerializer
return UserSerializer
def get_object(self):
return get_user(self.request)
@action(detail=True, methods=["post"], permission_classes=[IsAnonymous])
def register(self, request, *args, **kwargs):
"""Registering new User."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def change_password(self, request, *args, **kwargs):
"""Changes password."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail": "password changed"}, status=status.HTTP_200_OK)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def delete_user(self, request, *args, **kwargs):
"""Deletes user."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
user.delete()
return Response({"detail": "user deleted"}, status=status.HTTP_200_OK)
我相信这很明显,因为我几天前开始学习 drf,但搜索了几个小时 google 没有帮助。
首先你应该使用 self.request.user
而不是 get_user
。后者假定基于会话的身份验证,并且是会话身份验证后端用于将用户添加到请求对象的功能。无论身份验证后端如何,中间件都会为您获取用户并将其添加到请求对象中。
此外,您使用的是 JWTTokenUserAuthentication
而不是 JWTAuthentication
。后者会 return 你在你的请求中有一个 User
对象,而前者会在请求中放一个 TokenUser
。
我知道这不是第一个此类问题,但其他主题对我没有帮助。 我想使用 djangorestframework-simplejwt 对用户进行身份验证,一切都很好,因为它涉及到获取用户。我总是得到匿名用户。
我通过访问 api/token 并传递凭据来获取令牌。令牌创建正确,它包含正确的用户 ID。当我尝试访问受保护的视图时,它允许我访问,但 Django 无法识别用户。
settings.py
import os
from datetime import timedelta
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
# Quick-start development settings - unsuitable for production
# See https://docs.djangoproject.com/en/3.0/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = os.environ["SECRET_KEY"]
# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
ALLOWED_HOSTS = []
# Application definition
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"rest_framework",
"corsheaders",
"djmoney",
"bills",
"accounts",
]
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 = "shared_bills.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 = "shared_bills.wsgi.application"
# Database
# https://docs.djangoproject.com/en/3.0/ref/settings/#databases
DATABASES = {
"default": {
"ENGINE": "django.db.backends.sqlite3",
"NAME": os.path.join(BASE_DIR, "db.sqlite3"),
}
}
# Authentication
AUTH_USER_MODEL = "accounts.User"
# Password validation
# https://docs.djangoproject.com/en/3.0/ref/settings/#auth-password-validators
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator"
},
{"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator"},
{"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator"},
{"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator"},
]
# Internationalization
# https://docs.djangoproject.com/en/3.0/topics/i18n/
LANGUAGE_CODE = "en-us"
TIME_ZONE = "UTC"
USE_I18N = True
USE_L10N = True
USE_TZ = True
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = "/static/"
APPEND_SLASH = True
# CORS
CORS_ORIGIN_ALLOW_ALL = True
# Rest Framework
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["rest_framework.permissions.IsAuthenticated"],
"DEFAULT_RENDERER_CLASSES": ["rest_framework.renderers.JSONRenderer"],
"DEFAULT_AUTHENTICATION_CLASSES": [
"rest_framework_simplejwt.authentication.JWTTokenUserAuthentication"
],
}
if DEBUG:
REST_FRAMEWORK["DEFAULT_RENDERER_CLASSES"].append(
"rest_framework.renderers.BrowsableAPIRenderer"
)
REST_FRAMEWORK["DEFAULT_AUTHENTICATION_CLASSES"].extend(
[
"rest_framework.authentication.SessionAuthentication",
"rest_framework.authentication.BasicAuthentication",
]
)
# Simple JWT
SIMPLE_JWT = {
'ACCESS_TOKEN_LIFETIME': timedelta(weeks=1),
'REFRESH_TOKEN_LIFETIME': timedelta(days=1),
'ROTATE_REFRESH_TOKENS': False,
'BLACKLIST_AFTER_ROTATION': False,
'ALGORITHM': 'HS256',
'SIGNING_KEY': os.environ["SECRET_KEY"],
'VERIFYING_KEY': None,
'AUDIENCE': None,
'ISSUER': None,
'AUTH_HEADER_TYPES': ('Bearer',),
'USER_ID_FIELD': 'id',
'USER_ID_CLAIM': 'user_id',
'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',),
'TOKEN_TYPE_CLAIM': 'token_type',
'JTI_CLAIM': 'jti',
'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp',
'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5),
'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1),
}
views.py """Views for accounts application."""
from django.contrib.auth import get_user
from rest_framework import status
from rest_framework.decorators import action
from rest_framework.mixins import CreateModelMixin, RetrieveModelMixin
from rest_framework.permissions import BasePermission, IsAuthenticated
from rest_framework.response import Response
from rest_framework.viewsets import GenericViewSet
from accounts.models import User
from accounts.serializers import (
PasswordCheckSerializer,
UserChangePasswordSerializer,
UserSerializer,
)
class IsAnonymous(BasePermission):
"""Allows access only to anonymous users."""
def has_permission(self, request, view):
return not bool(request.user and request.user.is_authenticated)
class UserViewset(GenericViewSet, CreateModelMixin, RetrieveModelMixin):
"""Viewset for User object."""
queryset = User.objects.all()
def get_serializer_class(self):
if self.action == "register":
return UserSerializer
if self.action == "change_password":
return UserChangePasswordSerializer
if self.action == "delete_user":
return PasswordCheckSerializer
return UserSerializer
def get_object(self):
return get_user(self.request)
@action(detail=True, methods=["post"], permission_classes=[IsAnonymous])
def register(self, request, *args, **kwargs):
"""Registering new User."""
serializer = self.get_serializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def change_password(self, request, *args, **kwargs):
"""Changes password."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save()
return Response({"detail": "password changed"}, status=status.HTTP_200_OK)
@action(detail=True, methods=["post"], permission_classes=[IsAuthenticated])
def delete_user(self, request, *args, **kwargs):
"""Deletes user."""
user = self.get_object()
serializer = self.get_serializer(user, data=request.data)
serializer.is_valid(raise_exception=True)
user.delete()
return Response({"detail": "user deleted"}, status=status.HTTP_200_OK)
我相信这很明显,因为我几天前开始学习 drf,但搜索了几个小时 google 没有帮助。
首先你应该使用 self.request.user
而不是 get_user
。后者假定基于会话的身份验证,并且是会话身份验证后端用于将用户添加到请求对象的功能。无论身份验证后端如何,中间件都会为您获取用户并将其添加到请求对象中。
此外,您使用的是 JWTTokenUserAuthentication
而不是 JWTAuthentication
。后者会 return 你在你的请求中有一个 User
对象,而前者会在请求中放一个 TokenUser
。