Django之REST_FRAMEWORK 認證組件

Django之DRF之認證組件python

# from rest_framework.views import APIView
# APIView 中的  dispatch  中 執行的  self.initial(request,*args,**kwargs)中的 
# APIView---->dispatch------>self.initial------>三個校驗
        # self.perform_authentication(request)  # 認證校驗
        # self.check_permissions(request)  # 權限校驗
        # self.check_throttles(request)  # 頻率校驗
# 這裏的self 指的是我寫的那個序列化類

    def perform_authentication(self, request):
        # 這裏的request 是新的request  原生的request 在   新request._request 中
        """
        Perform authentication on the incoming request.

        Note that if you override this and simply 'pass', then authentication
        will instead be performed lazily, the first time either
        `request.user` or `request.auth` is accessed.
        """
        # 返回的user 是 新的request 中的一個 被假裝成屬性的方法(@property)
        # 想要超看這個 須要導包  from rest_frmework.request import Request
        request.user

    def check_permissions(self, request):
        """
        Check if the request should be permitted.
        Raises an appropriate exception if the request is not permitted.
        """
        for permission in self.get_permissions():
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )

這裏的user方法屬於 :git

from rest_framework.request import Request
# Request類中--->user方法----->裏面執行了  _authenticate(self) 方法

是 被假裝成數據屬性,django

@property
    def user(self):
        """
        Returns the user associated with the current request, as authenticated
        by the authentication classes provided to the request.
        """
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                # 這裏的self 是 Request 對象
                self._authenticate()  # 這裏執行的是 Request 類中的_authenticate()
        return self._user

重點的邏輯就是以下這段代碼:app

# APIView-->dispatch--->self.initial--->self.perform_authentication(request)
    # --->reuqest.user---->self._authenticate
    def _authenticate(self):
        """
        Attempt to authenticate the request using each authentication instance
        in turn.
        嘗試依次使用每一個身份驗證明例對請求進行身份驗證。
        """
        # 這裏的 self.autenticators 是一個元組,裏面存的是一個個實例化的對象
        # 這裏元組 是 Request 類在實例化的時候執行 __init__方法的時候賦值來的
        for authenticator in self.authenticators:
            try:
                # 這裏是執行本身寫的認證類中的的 authenticate()方法,獲得的是一個元組(這裏面存的是 user對象  和 一個auth對象)!!!若是有返回值的話
                user_auth_tuple = authenticator.authenticate(self)
                # 若是該方法中返回 user對象 和 auth對象 
            except exceptions.APIException:
                self._not_authenticated()  # 若是沒有經過認證走這個方法
                raise
           # 若是user_auth_tuple 有值,不爲空
            if user_auth_tuple is not None:
                self._authenticator = authenticator
                self.user, self.auth = user_auth_tuple
                return

        self._not_authenticated()

認證組件的使用

models.py

from django.db import models


# Create your models here.


class Book(models.Model):
    title = models.CharField(max_length=32)
    price = models.DecimalField(max_digits=8, decimal_places=2)
    publish_time = models.DateTimeField(auto_now_add=True)  # 自動添加建立時間
    authors = models.ManyToManyField('Author')
    publish = models.ForeignKey('Publish')  # 一對多
    
    def test(self):
        return self.title+'>>'+str(self.price)


class Author(models.Model):
    name = models.CharField(max_length=32)
    age = models.IntegerField()
    authordetail = models.OneToOneField('AuthorDetail')


class AuthorDetail(models.Model):
    tel_num = models.BigIntegerField()
    addr = models.CharField(max_length=32)


class Publish(models.Model):
    name = models.CharField(max_length=32)
    addr = models.CharField(max_length=32)
    email = models.EmailField()

# 用戶類
class User(models.Model):
    name = models.CharField(max_length=32)
    pwd = models.CharField(max_length=32)
# token類 與用戶類一對一關係
class Token(models.Model):
    user = models.OneToOneField(to='User')
    token = models.CharField(max_length=64)

認證類的創建(通常新建一個py文件)

from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from app01 import models

class MyAuth(BaseAuthentication):
    # 在認證組件中  須要重寫 authenticate方法來 完成 用戶認證邏輯
    def authenticate(self, request):
        # request值得是個對象
        token = request.GET.get('token')
        token_obj = models.Token.objects.filter(token=token).first()
        if token_obj:
            # 有值表示登陸了
            return
        else:
            # 沒有值,則報錯
            raise AuthenticationFailed('您沒有登陸')

views.pyide

from django.shortcuts import render
from django.http.response import JsonResponse

# Create your views here.
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.myser import BookSerializer
from app01 import auths
from django.core.exceptions import ObjectDoesNotExist
import uuid




class Book(APIView):
    # 這裏固定 須要些 這個認證類的列表,列表中存的是 一個一個認證的類
    authentication_classes = [auths.MyAuth,]
    # get 獲取全部書籍信息
    def get(self, request, id):
        response = {'status': 100, 'msg': '成功'}
        print(id)
        if not id:
            book_list = models.Book.objects.all()
            # 第一個參數是要序列化的queryset對象,若是要序列化多條,必須制定many=True
            # 當instance形參被傳入的實參是單個參數的時候,many=False
            book_serializer = BookSerializer(book_list, many=True)
        else:
            print(id)
            book_obj = models.Book.objects.filter(pk=id).first()
            book_serializer = BookSerializer(book_obj, many=False)
        print(book_serializer.data)
        response['books'] = book_serializer.data
        return Response(response)
    
    def post(self, request, id):
        response = {'status': 100, 'msg': '成功'}
        # 提交的字典
        book = request.data
        # 傳統方法,建立對象保存
        print(book)
        
        # 新方法,經過序列化組件保存,必須繼承自ModelSerializer
        book_ser = BookSerializer(data=book)
        # is_valid 提交的字段校驗經過
        if book_ser.is_valid():
            book_ser.save()
            response['book'] = book_ser.data
        else:
            response['msg'] = book_ser.errors  # errors  是序列化類 中的鉤子函數 raise來的報錯信息
        return Response(response)
    
    def put(self, request, id):
        response = {'status': 100, 'msg': '修改爲功'}
        if id:
            
            # 提交的字典
            book = request.data
            # 傳統方法,建立對象保存
            print(book)
            book_obj = models.Book.objects.filter(pk=id).first()
            
            # 新方法,經過序列化組件保存,必須繼承自ModelSerializer
            book_ser = BookSerializer(data=book, instance=book_obj)
            # is_valid 提交的字段校驗經過
            if book_ser.is_valid():
                # 這裏save()作修改
                book_ser.save()
                response['book'] = book_ser.data
            else:
                response['msg'] = book_ser.errors
        else:
            response['msg'] = '修改對象不存在'
        return Response(response)
    
    def delete(self, request, id):
        models.Book.objects.filter(pk=id).delete()
        response = {'status': 100, 'msg': '刪除成功'}
        return Response(response)

class Login(APIView):
    # 局部禁用 認證
    authentication_classes = []
    def post(self, request):
        response = {'code': 100, 'msg': '登陸成功'}
        name = request.data.get('name')
        pwd = request.data.get('pwd')
        print(name, pwd)
        try:
            # get()方法,獲取 有且只有一條的 纔不報錯,其餘狀況都拋異常
            ret = models.User.objects.filter(name=name, pwd=pwd).get()
            
            # 登陸成功後要去token 表中去存數據
            # 表裏有 數據或沒有數據
            # 1. 先生成隨機字符串 用uuid
            token = uuid.uuid4()
            # 2. 存入token表
            # update_or_create()  方法  先查後改,查到就修改,沒查到就新增  根據  user 去查
            models.Token.objects.update_or_create(user=ret, defaults={'token': token})
            response['token'] = token
        except ObjectDoesNotExist as exc:
            response['code'] = 101
            response['msg'] = '用戶名或密碼錯誤'
        except Exception as e:
            response['code'] = 102
            response['msg'] = str(e)
        return Response(response)

不存token的認證

def get_token(id,salt='123'):
    import hashlib
    md=hashlib.md5()
    md.update(bytes(str(id),encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))

    return md.hexdigest()+'|'+str(id)

def check_token(token,salt='123'):
    ll=token.split('|')
    import hashlib
    md=hashlib.md5()
    md.update(bytes(ll[-1],encoding='utf-8'))
    md.update(bytes(salt,encoding='utf-8'))
    if ll[0]==md.hexdigest():
        return True
    else:
        return False

class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        success=check_token(token)
        if success:
            return
        else:
            raise AuthenticationFailed('認證失敗')
    def authenticate_header(self,request):
        pass
class Login(APIView):
    def post(self,reuquest):
        back_msg={'status':1001,'msg':None}
        try:
            name=reuquest.data.get('name')
            pwd=reuquest.data.get('pwd')
            user=models.User.objects.filter(username=name,password=pwd).first()
            if user:
                token=get_token(user.pk)
                # models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
                back_msg['status']='1000'
                back_msg['msg']='登陸成功'
                back_msg['token']=token
            else:
                back_msg['msg'] = '用戶名或密碼錯誤'
        except Exception as e:
            back_msg['msg']=str(e)
        return Response(back_msg)
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
    def authenticate(self, request):
        token = request.GET.get('token')
        token_obj = models.UserToken.objects.filter(token=token).first()
        if token_obj:
            return
        else:
            raise AuthenticationFailed('認證失敗')
    def authenticate_header(self,request):
        pass

class Course(APIView):
    authentication_classes = [TokenAuth, ]

    def get(self, request):
        return HttpResponse('get')

    def post(self, request):
        return HttpResponse('post')

總結:

  • 局部使用: 只要在 須要認證的類裏面 固定寫入你所需的哪些認證(認證類)函數

    # 這裏固定 須要些 這個認證類的列表,列表中存的是 一個一個認證的類
        authentication_classes = [auths.MyAuth,]
  • 全局使用: 只要在settings文件中配置 REST_FRAMEWORK 便可post

    # settings.py 中配置
    REST_FRAMEWORK = {
        "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auths.MyAuth", ]
    }
  • 局部禁用:只要在不須要使用全局使用的認證的 視圖類中清除authentication_classes中你不須要的認證便可ui

    authentication_classes=[]

閱讀源碼體會:

  • 若是在settings.py 文件中配置了 REST_FRAMEWORK,先默認從項目settings中取
  • 若是取不到,纔去默認的drf配置文件中取
  • 用戶若是在 視圖類中配置了authentication_classes, 則優先從用戶配置的取

ps:先取視圖類中的配置的認證--->再取 項目settings文件中配置的----->最後取默認配置

相關文章
相關標籤/搜索