未提供 Angular + Django REST 身份验证凭据
Posted
技术标签:
【中文标题】未提供 Angular + Django REST 身份验证凭据【英文标题】:Angular + Django REST Authentication credentials were not provided 【发布时间】:2017-11-22 06:20:16 【问题描述】:我正在使用 Django REST 开发一个 API。客户端是在 node.js 中运行的 AngularJS 中的 SPA。注册和登录工作正常,但是当用户注销时显示错误消息:
"detail":"未提供身份验证凭据。"
我尝试了很多解决方案,例如 post 1 和 post 2。但问题仍在继续。如果我的 Angular 文件在服务器中,我的页面可以正常工作,但是当我更改为 yeoman 创建的 SPA 时,我遇到了凭据问题。
我的settings.py
import os
# 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/1.11/howto/deployment/checklist/
# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'y6u0gy4ij&8uoo6@p*$qukwp$-07@-1gd)@)(l!-j&wmpot4h#'
# 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',
'rest_framework.authtoken',
'authentication',
'corsheaders',
]
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.auth.middleware.SessionAuthenticationMiddleware', #
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
ROOT_URLCONF = 'ServerLearn.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',
],
,
,
]
REST_FRAMEWORK =
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.IsAuthenticated',
),
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
)
WSGI_APPLICATION = 'ServerLearn.wsgi.application'
# Database
# https://docs.djangoproject.com/en/1.11/ref/settings/#databases
DATABASES =
'default':
'ENGINE': 'django.db.backends.sqlite3',
'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
# Password validation
# https://docs.djangoproject.com/en/1.11/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/1.11/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/1.11/howto/static-files/
STATIC_URL = '/static/'
AUTH_USER_MODEL = 'authentication.Account'
CORS_ORIGIN_ALLOW_ALL = True
CORS_URLS_REGEX = r'^/api/v1/.*$'
我的models.py
from django.contrib.auth.models import AbstractBaseUser, BaseUserManager
from django.db import models
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
from django.conf import settings
# Create your models here.
@receiver(post_save, sender=settings.AUTH_USER_MODEL)
def create_auth_token(sender, instance=None, created=False, **kwargs):
if created:
token = Token.objects.create(user=instance)
print(token)
class AccountManager(BaseUserManager):
def create_user(self, username, password=None, **kwargs):
if not username:
raise ValueError("Need username.")
if not kwargs.get("email"):
raise ValueError("Need email.")
account = self.model(
username = username,
email = self.normalize_email(kwargs.get("email")),
name = kwargs.get("name"),
age = kwargs.get("age"),
gender = kwargs.get("gender"),
#birth = kwargs.get("birth")
)
account.set_password(password)
account.save()
return account
#admin
#senhaadmin
def create_superuser(self, username, password, **kwargs):
account = self.model(
username = username,
email = self.normalize_email(kwargs.get("email")),
name = "Admin",
age = 31111,
gender = 555,
)
account.is_admin = True
account.set_password(password)
account.save()
return account
class Account(AbstractBaseUser):
username = models.CharField(max_length = 50, unique = True)
email = models.EmailField(unique = True)
name = models.CharField(max_length = 100)
age = models.PositiveSmallIntegerField()
gender = models.PositiveSmallIntegerField()
#birth = models.DateField(null = True, blank = True)
created_at = models.DateTimeField(auto_now_add = True)
updated_at = models.DateTimeField(auto_now = True)
is_admin = models.BooleanField(default = False)
objects = AccountManager()
USERNAME_FIELD = 'username'
REQUIRED_FILES = ['username', 'email', 'name', 'age', 'gender']
def __unicode__ (self):
return self.username
我的views.py
class AccountViewSet(viewsets.ModelViewSet):
lookup_field = 'username'
queryset = Account.objects.all()
serializer_class = AccountSerializer
def get_permissions(self):
if self.request.method in permissions.SAFE_METHODS:
return (permissions.AllowAny(),)
if self.request.method == 'POST':
return (permissions.AllowAny(),)
return (permissions.IsAuthenticated(), IsAccountOwner(),)
def create(self, request):
serializer = self.serializer_class(data = request.data)
if serializer.is_valid():
Account.objects.create_user(**serializer.validated_data)
return Response(serializer.validated_data, status = status.HTTP_201_CREATED)
return Response(
'status': 'Bad request',
'message': 'Conta não pode ser criada'
, status = status.HTTP_400_BAD_REQUEST)
class LoginView(views.APIView):
def post(self, request, format=None):
data = json.loads(request.body.decode('utf-8'))
username = data.get('username', None)
password = data.get('password', None)
account = authenticate(username=username, password=password)
if account is not None:
if account.is_active:
login(request, account)
serialized = AccountSerializer(account)
return Response(serialized.data)
else:
return Response(
'status': 'Unauthorized',
'message': 'This account has been disabled.'
, status=status.HTTP_401_UNAUTHORIZED)
else:
return Response(
'status': 'Unauthorized',
'message': 'Username/password combination invalid.'
, status=status.HTTP_401_UNAUTHORIZED)
class LogoutView(views.APIView):
#ERROR IN NEXT LINE
permission_classes = (permissions.IsAuthenticated,)
def post(self, request, format=None):
logout(request)
return Response(, status=status.HTTP_204_NO_CONTENT)
在我的 Angular 应用程序中,在 app.js
中app.run(run);
run.$inject = ['$http'];
function run($http)
$http.defaults.xsrfHeaderName = 'X-CSRFToken';
$http.defaults.xsrfCookieName = 'csrftoken';
我使用代码登录后获得令牌:
$http.post('http://localhost:8000/api/v1/api-token-auth/',
username: username,
password: password
).then(tokenSuccessFn, tokenErrorFn);
function tokenSuccessFn(data, status, headers, config)
console.log("token: ");
console.log(JSON.stringify(data));
$http.defaults.headers.common.Authorization = 'Token ' + $cookies.get("csrftoken");
function tokenErrorFn(data, status, headers, config)
console.error('token error !!!');
退出是
return $http.post('http://localhost:8000/api/v1/auth/logout/')
.then(logoutSuccessFn, logoutErrorFn);
function logoutSuccessFn(data, status, headers, config)
Authentication.unauthenticate();
window.location = '/';
function logoutErrorFn(data, status, headers, config)
console.error('Logout error !!!');
【问题讨论】:
【参考方案1】:从你的 DRF DEFAULT_AUTHENTICATION_CLASSES 中删除 'rest_framework.authentication.SessionAuthentication'
,只使用 TokenAuth,如果你仍然需要可浏览的 api,你可以使用 chrome 插件 ModHeader。
【讨论】:
【参考方案2】:我发现了问题。我没有正确地将令牌添加到标题中。
我在本地存储中获取令牌和存储。
$http.post('http://localhost:8000/api/v1/api-token-auth/',
username: username,
password: password
).then(tokenSuccessFn, tokenErrorFn);
function tokenSuccessFn(data, status, headers, config)
localStorage.setItem('myApp.token',data['data'].token);
function tokenErrorFn(data, status, headers, config)
console.error('token error !!!');
当应用程序(页面)开始时,我从 app.js 中的本地存储加载令牌:
app.run(['$http', function ($http)
$http.defaults.headers.common['Authorization'] = 'Token ' + localStorage.getItem('myApp.token');
]);
感谢 Ykh 的帮助。
【讨论】:
以上是关于未提供 Angular + Django REST 身份验证凭据的主要内容,如果未能解决你的问题,请参考以下文章
Django REST框架+ Angular项目上的令牌认证
Django Rest Framework JWT“未提供身份验证凭据。”
Django Rest Framework:未提供身份验证凭据
Django Rest Framework JWT 未提供身份验证凭据