2017-06-19 166 views
0

我正在使用Django REST開發API。客戶端是在node.js中運行的AngularJS中的SPA。註冊並登錄做工精細,但是當用戶執行退出的錯誤消息表明:未提供Angular + Django REST身份驗證憑證

{「細節」:「不提供身份驗證憑據」}

我試過很多解決方案,如post 1post 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&[email protected]*[email protected])@)(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) 

在我的Angul AR應用,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 !!!'); 
} 

回答

0

從您的DRF DEFAULT_AUTHENTICATION_CLASSES刪除'rest_framework.authentication.SessionAuthentication',只使用TokenAuth,如果你仍然需要可瀏覽的api,你可以使用chrome插件n ModHeader

0

我發現了這個問題。我沒有正確地將標記添加到標頭。

我得到令牌和存儲在本地存儲。

$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的幫助。