权限校验和认证校验必须同时使用,而且权限校验是排在认证校验以后的,这在源码中能够查找到其执行顺序。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专享"] ) )
用户表数据以下:源码分析
电影表数据以下:post
采用模型类序列器,而且只对电影作序列化:测试
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
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
这个用户,他应该只能看到免费电影,而相似泰坦尼克号这种电影是不该该让他看见的。
写一个类,继承BasePermission
,重写has_permission()
方法,若是权限经过,就返回True
,不经过就返回False
。
接下来咱们来写权限校验,has_permission()
方法接受两个参数,分别是request
和view
,也就是视图类的实例化自己。
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到请求头中
再次使用jack
的token
进行登陆,能够发现他只会看到免费电影了。
而使用ken
的token
进行登陆,他将看不到钻石VIP的电影,如泰坦尼克号:
假若使用yunya
的token
进行登陆,则能够查看到全部的电影。
若是你是使用auth
组件来作的一系列登陆等,则能够使用内置权限。
它会判断该用户的is_staff
字段是否为True
。
内置权限的类使用有不少,以下所示:
# 演示一下内置权限的使用: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) )