Token認證——Django Rest framework(1)

RomanceSky發表於2018-05-30

Django REST Framework 中的Token認證

1、使用knox包生成token

2、使用django restframework中的Token類生成token

3、自定義

1、使用knox包生成token

使用REST Framework 的第三方庫Knox進行TokenAuthentication認證

Knox是基於TokenAuthentication認證的

未加TokenAuthentication認證的完整例項

專案

Floral
   Floral
      urls.py.py
      ...
   accounts
     views.py
   mange.py
複製程式碼
app下的serializers.py:

from django.contrib.auth.models import User, Group
from rest_framework import serializers


class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'groups')


class GroupSerializer(serializers.HyperlinkedModelSerializer):
    user = serializers.SerializerMethodField()
    class Meta:
        model = Group
        fields = ('url', 'name', 'user')

app下的views.py:
from django.shortcuts import render
# Create your views here.

from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from Users.serializers import UserSerializer, GroupSerializer

class UserViewSet(viewsets.ModelViewSet):
    """
    允許使用者檢視或編輯的API路徑。
    """
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer


class GroupViewSet(viewsets.ModelViewSet):
    """
    允許組檢視或編輯的API路徑。
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer
    
專案下的urls.py:

from django.conf.urls import url, include
from rest_framework import routers
from Users import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# 使用自動URL路由連線我們的API。
# 另外,我們還包括支援瀏覽器瀏覽API的登入URL。
urlpatterns = [
    url(r'^', include(router.urls)),
    url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

複製程式碼

測試: 使用第三方庫庫Knox 新增Token認證的完整例項 需求: 1、登入建立token 2、建立使用者的時候建立token 介面呼叫http://127.0.0.1:8000/users/

tips:一般建立使用者的使用不會設定token認證

專案下的urls.py:

from django.conf.urls import url, include
from rest_framework import routers
from accounts import views

router = routers.DefaultRouter()
router.register(r'users', views.UserViewSet)
router.register(r'groups', views.GroupViewSet)

# 使用自動URL路由連線我們的API。
# 另外,我們還包括支援瀏覽器瀏覽API的登入URL。
urlpatterns = [
    url(r'api/auth/', include('knox.urls')),
    url(r'^', include(router.urls)),
    # url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework'))
]

app下的views.py:

from django.shortcuts import render
# Create your views here.
from django.contrib.auth.models import User, Group
from rest_framework import viewsets
from accounts.serializers import UserSerializer, GroupSerializer
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from knox.auth import TokenAuthentication
from knox.models import AuthToken
from rest_framework.permissions import IsAuthenticated, AllowAny

class UserViewSet(viewsets.ModelViewSet):
    authentication_classes = (
        TokenAuthentication,
    )
    permission_classes = (AllowAny,)
    queryset = User.objects.all().order_by('-date_joined')
    serializer_class = UserSerializer

    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        auth_token = AuthToken.objects.create(user)
        # print(auth_token)
        return Response(
            {
                "email": user.email,
                "token": auth_token,
                "id": user.id,
                #"key": auth_token.key,
            }
        )

class GroupViewSet(viewsets.ModelViewSet):
    """
    允許組檢視或編輯的API路徑。
    """
    queryset = Group.objects.all()
    serializer_class = GroupSerializer
    
    
app下的serialziers.py:

from django.contrib.auth.models import User, Group
from rest_framework import serializers

class UserSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = User
        fields = ('url', 'username', 'email', 'groups')


class GroupSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Group
        fields = ('url', 'name')
        
        

settings.py:

"""
Django settings for Floral project.

Generated by 'django-admin startproject' using Django 2.0.5.

For more information on this file, see
https://docs.djangoproject.com/en/2.0/topics/settings/

For the full list of settings and their values, see
https://docs.djangoproject.com/en/2.0/ref/settings/
"""
from datetime import timedelta
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/2.0/howto/deployment/checklist/

# SECURITY WARNING: keep the secret key used in production secret!
SECRET_KEY = 'vz1_87zl^pczk=d#8&e(!e8!yu4d$@ejd+x@hogcfbq7y$j*u^'

# SECURITY WARNING: don't run with debug turned on in production!
DEBUG = True
#AUTH_USER_MODEL = 'accounts.Myaccounts'

ALLOWED_HOSTS = ['*']
from dotenv import load_dotenv

load_dotenv()
from pathlib import Path  # python3 only
env_path = Path('.') / '.env'
load_dotenv(dotenv_path=env_path)
# Application definition

ADMIN_LOGIN = 'admin'
ADMIN_PASSWORD = 'pbkdf2_sha256$30000$Vo0VlMnkR4Bk$qEvtdyZRWTcOsCnI/oQ7fVOu1XAURIZYoOZ3iq8Dr4M='
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'Floral',
    'accounts',
    'django_filters',
    'knox',
    'rest_framework.authtoken',
]

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',
]

ROOT_URLCONF = 'Floral.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 = 'Floral.wsgi.application'


# Database
# https://docs.djangoproject.com/en/2.0/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myjun',
        'USER': 'charolim',
        'HOST': '127.0.0.1',
    }
}


# Password validation
# https://docs.djangoproject.com/en/2.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/2.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/2.0/howto/static-files/

STATIC_URL = '/static/'

REST_FRAMEWORK = {
    'DEFAULT_RENDERER_CLASSES':(
         'rest_framework.renderers.JSONRenderer',
         'rest_framework.renderers.BrowsableAPIRenderer',
    ),
    'DEFAULT_FILTER_BACKENDS': ('django_filters.rest_framework.DjangoFilterBackend',),

    'DEFAULT_PARSER_CLASSES':(
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),

    'DEFAULT_PERMISSION_CLASSES':(
        'rest_framework.permissions.AllowAny',
    ),

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    ),

    # 'DEFAULT_AUTHENTICATION_CLASSES': ('knox.auth.TokenAuthentication',),
}

# ############ REST KNOX ########################
REST_KNOX = {
  'SECURE_HASH_ALGORITHM': 'cryptography.hazmat.primitives.hashes.SHA512',
  'AUTH_TOKEN_CHARACTER_LENGTH': 64,
  'TOKEN_TTL': timedelta(hours=10),
  'USER_SERIALIZER': 'knox.serializers.UserSerializer',
}

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG' if DEBUG else 'INFO',
        },
    },
}

複製程式碼

使用Postman測試

介面http://127.0.0.1:8000/users/
GET 
對於檢視,使用者顯示的時候建立token?(這裡自己也未明白)
    def list(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        auth_token = AuthToken.objects.create(user)
        # print(auth_token)
        return Response(
            {
                "email": user.email,
                "token": auth_token,
                "id": user.id,
                #"key": auth_token.key,
            }
        )
結果

[
    {
        "url": "http://127.0.0.1:8000/users/13/",
        "username": "charolim1212",
        "email": "",
        "groups": []
    },
]

介面http://127.0.0.1:8000/users/
POST 
對於檢視,使用者顯示的時候建立token?(這裡自己也未明白)
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()
        auth_token = AuthToken.objects.create(user)
        # print(auth_token)
        return Response(
            {
                "email": user.email,
                "token": auth_token,
                "id": user.id,
                #"key": auth_token.key,
            }
        )
結果
key 是username
value是你要建立的使用者名稱如charm
提示要認證
![圖](https://note.youdao.com/yws/res/32075/WEBRESOURCEf70b8348446306674e5297af54359d49)
認證後
![圖](https://note.youdao.com/yws/res/32071/WEBRESOURCEc22df0ed54f48706f015b597756ec40e)

複製程式碼

2、使用django restframework中的Token類生成token

安裝和配置djoser
[https://github.com/sunscrapers/djoser]
完整例項
專案結構: 
   Floral專案名
     app01(app名)
     manage.py
settings.py:
INSTALLED_APPS = [
'rest_framework',
    'rest_framework_xml',
    'app01',
    'djoser',
    'rest_framework.authtoken',
    'django_filters',

]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql',
        'NAME': 'cobra_dev',
        'USER': 'charo',
        'HOST': '127.0.0.1',
    }
}


REST_FRAMEWORK = {

    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.TokenAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework.authentication.SessionAuthentication',
    ),
    #
    # 'DEFAULT_PARSER_CLASSES': (
    #     'rest_framework_xml.parsers.XMLParser',
    #     'rest_framework.parsers.JSONParser',
    #
    # ),

    'DEFAULT_PARSER_CLASSES': (
        'rest_framework.parsers.JSONParser',
        'rest_framework.parsers.FormParser',
        'rest_framework.parsers.MultiPartParser'
    ),
    'DEFAULT_RENDERER_CLASSES': (
        # 'rest_framework_xml.renderers.XMLRenderer',
        'rest_framework.renderers.JSONRenderer',

    ),

}
AUTH_USER_MODEL = 'app01.Membership'
# ############ REST DJOSER ########################

DJOSER = {
    'PASSWORD_RESET_CONFIRM_URL': '#/password/reset/confirm/{uid}/{token}',
    'ACTIVATION_URL': '#/activate/{uid}/{token}',
    'SEND_ACTIVATION_EMAIL': True,
    'SERIALIZERS': {},
}


# ############ Logging ########################

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'handlers': {
        'console': {
            'class': 'logging.StreamHandler',
        },
    },
    'loggers': {
        'django.db.backends': {
            'handlers': ['console'],
            'level': 'DEBUG' if DEBUG else 'INFO',
        },
    },

models.py:

from django.db import models
from django.contrib.auth.models import AbstractBaseUser
class Membership(AbstractBaseUser):
    USERNAME_FIELD = 'mobileno'
    REQUIRED_FIELDS = ('firstname', 'lastname')

    name = models.CharField(verbose_name='會員名稱', max_length=32, null=True, blank=True)
    firstname = models.CharField(verbose_name='FirstName', max_length=32, null=True, blank=True)
    role = models.ForeignKey(verbose_name="角色", to='Role', related_name='membership', on_delete=models.CASCADE)

class Role(models.Model):
    """
    角色表
    """
    ADMIN, POS,  MEMBERSHIP, ANONYMOUS = range(4)
    name_choices = (
        (ADMIN, "admin"),
        (POS, "pos"),
        (MEMBERSHIP, "membership"),
        (ANONYMOUS, "anonymous"),
    )
    name = models.IntegerField(
        verbose_name="角色名稱",
        choices=name_choices,
        default=MEMBERSHIP,
        help_text="選擇角色名稱"
    )
   
views.py
from rest_framework import viewsets
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from django.contrib.auth.models import User

from .serializers import MembershipSerializer
from .models import Membership
from rest_framework.authtoken.models import Token

from rest_framework import viewsets
from djoser.views import UserCreateView, TokenCreateView, TokenDestroyView
from rest_framework.authtoken.models import Token
from rest_framework import permissions, status
from djoser import utils, signals
from djoser.compat import get_user_email, get_user_email_field_name
from djoser.conf import settings
from rest_framework import viewsets
from rest_framework.response import Response
from django.contrib.auth.models import User

from .serializers import MembershipSerializer
from .models import Membership

#
# class MembershipCreateView(UserCreateView):
#     serializer_class = MembershipSerializer
#     permission_classes = [permissions.AllowAny]
#
#
#     def perform_create(self, serializer):
#         user = serializer.save()
#         signals.user_registered.send(
#             sender=self.__class__, user=user, request=self.request
#         )
#
#         context = {'user': user}
#         to = [get_user_email(user)]
#         if settings.SEND_ACTIVATION_EMAIL:
#             settings.EMAIL.activation(self.request, context).send(to)
#         elif settings.SEND_CONFIRMATION_EMAIL:
#             settings.EMAIL.confirmation(self.request, context).send(to)
#


class MembershipCreateViewSet(viewsets.ModelViewSet):
#     authentication_classes = (
#         Token,
#     )
    queryset = Membership.objects.all().only('mobileno')
    serializer_class = MembershipSerializer

    #def create(self, request, *args, **kwargs):
    #     serializer = self.get_serializer(data=request.data, many=True)
    #     print(request.data)
    #     serializer.is_valid(raise_exception=True)
    #     serializer.save()
    #     return Response(serializer.data)
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()

        # username = serializer.validated_data['username']
        # print(username)
        # Membership object(5)
        # token = Token.objects.all()[0]
        # user = token.user
        token, created = Token.objects.get_or_create(user=user)
        # super(MembershipCreateViewSet, self).create()
        return Response(
            {
                "email": user.email,
                "token": token.key,
                "id": user.id,
            }
        )
        
serializers.py
from rest_framework import serializers

from .models import Membership


class MembershipSerializer(serializers.ModelSerializer):

    class Meta:
        model = Membership
        fields = '__all__'


urls.py(app01應用下的)

from django.urls import path, include
from rest_framework.routers import DefaultRouter


from .views import MembershipCreateViewSet

app_name = "app01"


memberrouter = DefaultRouter()
memberrouter.register(r'', MembershipCreateViewSet,
                      base_name='membership')

urlpatterns = [
    path(r'auth/', include('djoser.urls')),
    path(r'auth/', include('djoser.urls.authtoken')),
    path(r'member/', include(memberrouter.urls)),
 ]
 
urls.py(專案下的)
 
from django.contrib import admin
from django.urls import path, include
from rest_framework.routers import DefaultRouter


urlpatterns = [
    path('admin/', admin.site.urls),
    # path('api/test', views.TestView.as_view()),
    path('app01/', include('app01.urls')),

]

notes:
python manage.py shell下
>>> from app01.models import Role, Membership
>>> role = Role.objects.create(name=1)
>>> user = Membership.objects.create(name='charolim', role=role)
>>> user
<Membership: >
>>> user.id
2
>>> user.name
'charolim'
>>> 
建立資料後,再在檢視中
def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.save()

        # username = serializer.validated_data['username']
        # print(username)
        # Membership object(5)
        # token = Token.objects.all()[0]
        # user = token.user
        token, created = Token.objects.get_or_create(user=user)
        # super(MembershipCreateViewSet, self).create()
        return Response(
            {
                "email": user.email,
                "token": token.key,
                "id": user.id,
            }
        )
        

使用Token類進行Token的生成,需要在settings.py中增加
 'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
複製程式碼

使用Postman測試membership的建立(同時生成並返回token)http://127.0.0.1:8000/app01/member/

結果
{
    "email": null,
    "token": "c6c17a4634d826c4b4e7367b06c6f5c6ed22d159",
    "id": 3
}
圖片https://note.youdao.com/yws/res/32313/WEBRESOURCEb4a6b35887f5092e474ee9515d323094
複製程式碼

【參考https://www.scienjus.com/my-restful-api-best-practices/】

相關文章