1. 程式人生 > >Rest Framework:四、認證元件

Rest Framework:四、認證元件

一、建立LoginAuth類完成認證過程

urls.py

url(r'^login/', views.Login.as_view()),
url(r'^books/', views.Books.as_view()),

models.py

from django.db import models

# Create your models here.
class UserInfo(models.Model):
    name = models.CharField(max_length=32)
    # 寫choice
    user_choice=((0,'普通使用者'),(1,'會員'),(2,'超級使用者'))
    # 指定choice,可以快速的通過數字,取出文字
    user_type=models.IntegerField(choices=user_choice,default=0)
    pwd = models.CharField(max_length=32)

class UserToken(models.Model):
    token = models.CharField(max_length=64)
    user = models.OneToOneField(to=UserInfo)

class Book(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=5, decimal_places=2)
    publish_date = models.DateField()

    publish = models.ForeignKey(to='Publish', to_field='nid', on_delete=models.CASCADE)
    authors = models.ManyToManyField(to='Author')

    def __str__(self):
        return self.name


class Author(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    author_detail = models.OneToOneField(to='AuthorDatail', to_field='nid', unique=True, on_delete=models.CASCADE)


class AuthorDatail(models.Model):
    nid = models.AutoField(primary_key=True)
    telephone = models.BigIntegerField()
    birthday = models.DateField()
    addr = models.CharField(max_length=64)


class Publish(models.Model):
    nid = models.AutoField(primary_key=True)
    name = models.CharField(max_length=32)
    city = models.CharField(max_length=32)
    email = models.EmailField()

    def __str__(self):
        return self.name

    def test(self):
        return self.email

遷移資料庫

python3 manage makemigrations
python3 manage migrate


app01/MySerializer.py

from rest_framework import serializers
from app01 import models
class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields='__all__'

views.py

from django.shortcuts import render
from django.http import JsonResponse
from rest_framework.views import APIView
from app01 import models
import hashlib
import time
from django.core.exceptions import ObjectDoesNotExist
from app01 import MySerializer


# Create your views here.

def get_token(name):
    # 生成一個md5物件
    md5 = hashlib.md5()
    # 往裡新增值,必須是bytes格式
    # time.time()生成時間戳型別,轉成字串,再encode轉成bytes格式
    md5.update(str(time.time()).encode('utf-8'))
    md5.update(name.encode('utf-8'))
    return md5.hexdigest()


class Login(APIView):
    authentication_classes = []

    def post(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '登入成功'}
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        try:
            user = models.UserInfo.objects.get(name=name, pwd=pwd)
            # 校驗通過,登入成功,生成一個隨機字串(身份標識)token
            token = get_token(name)
            # 儲存到資料庫
            # update_or_create更新或者建立
            models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
            response['token'] = token
        except ObjectDoesNotExist as e:
            response['status'] = 101
            response['msg'] = '使用者名稱或密碼錯誤'
        except Exception as e:
            response['status'] = 102
            # response['msg']='未知錯誤'
            response['msg'] = str(e)
        return JsonResponse(response, safe=False)


from rest_framework import exceptions


class LoginAuth():
    # 函式名必須叫這個名字,接收必須兩個引數,第二個引數是request對像
    def authenticate(self, request):
        # 從request對像中取出token (也可以從其他地方中取)
        token = request.query_params.get('token')
        # 去資料庫中查詢
        ret = models.UserToken.objects.filter(token=token)
        if ret:
            # 可以查到,說明認證通過,返回空
            return None
        #   否則會報異常
        raise exceptions.APIException('認證失敗')


from rest_framework.request import Request


# class Books(APIView):
#     # 列表中型別不能加括號
#     authentication_classes = [LoginAuth, ]
#
#     def get(self, request, *args, **kwargs):
#         response = {'status': 100, 'msg': '查詢成功'}
#         # 必須登入以後,才能獲取資料
#         # 取出token,取資料庫驗證,是否登入
#         token = request.query_params.get('token')
#         ret = models.UserToken.objects.filter(token=token)
#         if ret:
#             # 認證通過,是登入使用者
#             ret = models.Book.objects.all()
#             book_ser = MySerializer.BookSerializer(ret, many=True)
#             response['data'] = book_ser.data
#         else:
#             response['status'] = 101
#             response['msg'] = '認證不通過'
#         return JsonResponse(response, safe=False)

# 與上面的Books類功能一樣,但比上面的那個更加簡潔。
class Books(APIView):
    # 列表中型別不能加括號
    authentication_classes = [LoginAuth, ]

    def get(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '查詢成功'}
        ret = models.Book.objects.all()
        book_ser = MySerializer.BookSerializer(ret, many=True)
        response['data'] = book_ser.data
        return JsonResponse(response, safe=False)

資料庫中新增資料測試:

圖片.png

圖片.png

圖片.png

使用Postman測試:

功能:登入後方可檢視書的詳情資訊

使用資料庫中的使用者名稱與密碼登入服務:

圖片.png

使用token登入來驗證查詢書的詳情資訊

圖片.png

若是token驗證不成功,就會返回:

圖片.png

二、認證元件的全域性使用與區域性使用

urls.py、models.py、不變

區域性使用:

views.py

from app01.MyAuth import LoginAuth
class Books(APIView):
    # 列表中型別不能加括號
    # 認證元件區域性使用
    authentication_classes = [LoginAuth, ]
    # 認證元件,區域性禁用 authentication_classes = []
    def get(self, request, *args, **kwargs):
        response = {'status': 100, 'msg': '查詢成功'}
        ret = models.Book.objects.all()
        book_ser = MySerializer.BookSerializer(ret, many=True)
        response['data'] = book_ser.data
        return JsonResponse(response, safe=False)

app01/MyAuth.py

from rest_framework import exceptions
from app01 import models
# 用dnf 認證,寫一個類
class LoginAuth():
    # 函式名必須叫這個名字,接收必須兩個引數,第二個引數是request對像
    def authenticate(self, request):
        # 從request對像中取出token (也可以從其他地方中取)
        token = request.query_params.get('token')
        # 去資料庫中查詢
        ret = models.UserToken.objects.filter(token=token)
        if ret:
            # 可以查到,說明認證通過,返回空
            return None
        #   否則會報異常
        raise exceptions.APIException('認證失敗')

-全域性使用
    -在setting中配置:

REST_FRAMEWORK={
    'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',],
}

-區域性禁用,views.py中有:

認證元件,區域性禁用 authentication_classes = []