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)
                )
相关文章
相关标签/搜索