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/】