drf 權限校驗設置與源碼分析

權限校驗

   權限校驗和認證校驗必須同時使用,而且權限校驗是排在認證校驗以後的,這在源碼中能夠查找到其執行順序。django

   權限校驗也很重要,認證校驗能夠確保一個用戶登陸以後才能對接口作操做,而權限校驗能夠依據這個登陸用戶的類型來限定能對接口作那些操做。api

準備工做

模型表

   下面是模型表,對於不一樣等級的用戶,訪問同一url,可以獲取到的電影內容也不同。app

from django.db import models


# Create your models here.

class User(models.Model):
    user_id = models.AutoField(primary_key=True)
    user_name = models.CharField(max_length=32)
    user_password = models.CharField(max_length=32)
    user_type = models.IntegerField(choices=(
        [0,"普通用戶"],
        [1,"黃金VIP"],
        [2,"鑽石VIP"],
    ))
    user_token = models.CharField(max_length=64, null=True, unique=True)

class Film(models.Model):
    film_id = models.AutoField(primary_key=True)
    film_name = models.CharField(max_length=32)
    film_grade = models.IntegerField(
        choices=(
            [0,"免費電影"],
            [1,"黃金VIP專享"],
            [2,"鑽石VIP專享"]
        )
    )

   用戶表數據以下:源碼分析

   image-20201101155300389

   電影表數據以下:post

   image-20201101155452968

序列類

   採用模型類序列器,而且只對電影作序列化:測試

from rest_framework import serializers
from app01 import models

class FilmModelSerializers(serializers.ModelSerializer):
    class Meta:
        model = models.Film
        fields = "__all__"

認證校驗

   下面是認證權限,只有登陸後的用戶才能夠看到電影信息。若是沒有登陸將不容許查看全部電影的頁面,或者你也能夠不設置認證校驗,可是在權限設置中能夠設置匿名用戶直接返回一個False也行。網站

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

class LoginAuth(BaseAuthentication):
    def authenticate(self,request):
        token = request.META.get("HTTP_TOKEN")
        if not token:
            # 未登陸
            raise APIException("未登陸,沒法查看本電影網站,請先登陸")
        user_obj = models.User.objects.filter(user_token=token).first()
        if not user_obj:
            raise APIException("token無效,登陸失敗")
        else:
            return user_obj,user_obj.user_type  # 將用戶對象自己以及用戶的類型存儲到request.user以及request.auth中

視圖

   下面是視圖的代碼:ui

from uuid import uuid4

from rest_framework.generics import ListAPIView
from rest_framework.generics import GenericAPIView
from rest_framework.views import APIView
from rest_framework.views import Response

from app01 import models
from app01 import serializationClass
from app01 import app01_auth


class FilmAPI(ListAPIView,GenericAPIView):
    authentication_classes = [app01_auth.LoginAuth]  # 必須登陸
    queryset = models.Film.objects.all()  # 默認查看全部,任何用戶都是
    serializer_class = serializationClass.FilmModelSerializers
    
    def get(self,request):
        return self.list(request)


class Login(APIView):
    def post(self,request):
        user_dict = {
            "user_name":request.data.get("user_name"),
            "user_password":request.data.get("user_password"),
        }
        user_obj = models.User.objects.filter(**user_dict).first()
        if not user_obj:
            return Response(data="用戶名或密碼錯誤")
        else:
            token = uuid4()  # 建立token
            user_obj.user_token = token
            user_obj.save()
            return Response(data="登陸成功",headers={"token":token})  # 返回token到請求頭中

url

   下面是路由。url

from django.contrib import admin
from django.urls import path

from app01 import views


urlpatterns = [
    path('admin/', admin.site.urls),
    path('login/',views.Login.as_view()),
    path('api/film/',views.FilmAPI.as_view()),
]

簡單嘗試

   如今咱們啓動django項目試試。能夠發現,當咱們在請求頭中設置好token並朝api發送GET請求時,它會返回全部的數據。spa

   咱們登陸的是jack這個用戶,他應該只能看到免費電影,而相似泰坦尼克號這種電影是不該該讓他看見的。

   image-20201101162251081

自定製權限

   寫一個類,繼承BasePermission,重寫has_permission()方法,若是權限經過,就返回True,不經過就返回False

權限校驗

   接下來咱們來寫權限校驗,has_permission()方法接受兩個參數,分別是requestview,也就是視圖類的實例化自己。

from rest_framework.permissions import BasePermission
from app01 import models
from django.db.models import Q

class UserPermission(BasePermission):
    def has_permission(self, request, view):
        if request.auth == 1:  # 若是是普通用戶,修改當前獲取的資源爲免費電影
            view.queryset = models.Film.objects.filter(film_grade=1)
        elif request.auth == 2:  # 若是是黃金VIP,則只能獲取黃金VIP電影和免費電影
            view.queryset = models.Film.objects.filter(Q(film_grade=2) | Q(film_grade=1))
        else:
            pass  # 默認就是獲取全部,因此不用修改

        return True  # 權限校驗完成,設置好了。普通用戶只能看免費電影

局部使用

   下面是局部使用,只須要用一個變量名爲permission_classes的列表,將權限校驗的類放入便可:

class FilmAPI(ListAPIView,GenericAPIView):
    authentication_classes = [app01_auth.LoginAuth]  # 必須登陸
    permission_classes = [app01_permissions.UserPermission]  # 作權限設置

    queryset = models.Film.objects.all()  # 默認查看全部,任何用戶都是
    serializer_class = serializationClass.FilmModelSerializers

    def get(self,request):
        return self.list(request)

全局使用

   若是是全局使用,則須要到項目全局文件夾下的settings.py中進行設置:

# 全局使用
REST_FRAMEWORK={
    'DEFAULT_PERMISSION_CLASSES': [
        'app01.app01_permissions.UserPermission',
    ],
}

   若是想取消某個接口的權限認證設置,則在其中設置類屬性permission_classes是一個空列表。

   以下所示,登陸功能不須要驗證權限,咱們對他取消掉便可。

class Login(APIView):
	permission_classes = []  # 取消權限驗證設置
	
    def post(self,request):
        user_dict = {
            "user_name":request.data.get("user_name"),
            "user_password":request.data.get("user_password"),
        }
        user_obj = models.User.objects.filter(**user_dict).first()
        if not user_obj:
            return Response(data="用戶名或密碼錯誤")
        else:
            token = uuid4()  # 建立token
            user_obj.user_token = token
            user_obj.save()
            return Response(data="登陸成功",headers={"token":token})  # 返回token到請求頭中

結果演示

   再次使用jacktoken進行登陸,能夠發現他只會看到免費電影了。

   image-20201101164843974

   而使用kentoken進行登陸,他將看不到鑽石VIP的電影,如泰坦尼克號:

   image-20201101165031651

   假若使用yunyatoken進行登陸,則能夠查看到全部的電影。

   image-20201101164937760

內置權限

   若是你是使用auth組件來作的一系列登陸等,則能夠使用內置權限。

   它會判斷該用戶的is_staff字段是否爲True

   內置權限的類使用有不少,以下所示:

   image-20201101164309110

基本演示

# 演示一下內置權限的使用:IsAdminUser,控制是否對網站後臺有權限的人
# 1 建立超級管理員
# 2 寫一個測試視圖類

from rest_framework.permissions import IsAdminUser
from rest_framework.authentication import SessionAuthentication
class TestView3(APIView):
    authentication_classes=[SessionAuthentication,]  # 必須有認證
    permission_classes = [IsAdminUser]  # 權限設置
    def get(self,request,*args,**kwargs):
        return Response('這是22222222測試數據,超級管理員能夠看')
        
# 3 超級用戶登陸到admin,再訪問test3就有權限
# 4 正常的話,普通管理員,沒有權限看(判斷的是is_staff字段)

源碼分析

   權限校驗排在認證校驗以後,這在源碼中能夠查看到。

   它的源碼相比於認證的源碼來講簡單的多,認證的執行是在request.user中進行的,而它直接是在當前視圖類中進行,因此很簡單。

# APIView---->dispatch---->initial--->self.check_permissions(request)(APIView的對象方法)
    def check_permissions(self, request):
        # 遍歷權限對象列表獲得一個個權限對象(權限器),進行權限認證 遍歷  for (權限認證的實例化對象)
        for permission in self.get_permissions():
            # 權限類必定有一個has_permission權限方法,用來作權限認證的
            # 參數:權限對象self、請求對象request、視圖類對象
            # 返回值:有權限返回True,無權限返回False
            if not permission.has_permission(request, self):
                self.permission_denied(
                    request, message=getattr(permission, 'message', None)
                )
相關文章
相關標籤/搜索