Django-restframework 之权限源码分析
一 前言
上篇博客分析了 restframework 框架的认证组件的执行了流程并自定义了认证类。这篇博客分析 restframework 的权限组件执行流程。入口函数依然是APIView.initial
。
权限的判断是在用户认证之后进行的,restframework 框架里面的自带的认证实现的功能很简单,如下:
这个方法通常来讲会根据需求定制,该方法执行结束后悔返回用户信息和其他数据,根据需求,在上篇博客我返回了用户对象和 token 值。
本来这些应该在上篇博客结束的,主要是因为今天学习权限组件时又想到有些遗漏所以在这里补充。
二 权限组件执行流程
APIView.perform_authentication()
方法结束,其实是根据mro
列表找到的。接下来执行APIView.check_permissions()
方法,restframework 框架自带的权限类相当于没有,因为所有需要进行权限认证的都是返回 True,所以这个也需要根据实际需求来定制。
1. 执行 APIView.check_permissions
其实套路和认证组件很相似
2. 执行APIView.get_permission
对比权限和认证的查找相关类的流程可以发现认证类是在实例化Request
对象时就把认证类获取当作参数传进去了,而权限类并没有。猜测 restframework 框架只要需要进行用户认证,所以虽然认证写的功能也不完善,但是还是必要的,而权限相关的认证和实际需求有关,所以就没有这么麻烦,猜测频率也一样,也需要自己重写和配置。
3. 执行APIView.permission_classes
经过这几步就可以找到权限类
三 自定义权限组件
其实基本步骤和认证组件一样
1. views.py
from app01 import permiss_classes
2. permiss_classes.py
from rest_framework.permissions import BasePermission
class LoginPermission(BasePermission):
def has_permission(self, request, view):
print(request.user, 'guanjian')
user = request.user
if user.user_type == 1:
return True
return False
3. 使用
只要用户权限满足才能获取相关信息,所以在用户表中加了个字段用来标识用户权限的
# models.py
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
password = models.CharField(max_length=32)
age = models.IntegerField()
gender = models.SmallIntegerField()
book = models.ManyToManyField(to='Book', through='User2Book', through_fields=('user', 'book'))
user_choice = ((0, '封禁用户'), (1, '普通用户'), (2, '超级用户'))
user_type = models.IntegerField(default=0, choices=user_choice)
完整的 models.py
from django.db import models
# Create your models here.
# book_obj.author.set(*[])
# class UserToken(models.Model):
# nid = models.AutoField(primary_key=True)
# user = models.OneToOneField(to='UserInfo', default=1)
# token = models.CharField(max_length=64, default='123456')
class UserInfo(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
password = models.CharField(max_length=32)
age = models.IntegerField()
gender = models.SmallIntegerField()
book = models.ManyToManyField(to='Book', through='User2Book', through_fields=('user', 'book'))
user_choice = ((0, '封禁用户'), (1, '普通用户'), (2, '超级用户'))
user_type = models.IntegerField(default=0, choices=user_choice)
def __str__(self):
return self.name
class Meta:
verbose_name = '用户表'
verbose_name_plural = verbose_name
# 用户拥有的图书表,因为是多对多关系,所以是中间表
class User2Book(models.Model):
nid = models.AutoField(primary_key=True)
user = models.ForeignKey(to='UserInfo')
book = models.ForeignKey(to='Book')
class Book(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
price = models.IntegerField()
publish = models.ForeignKey(to='Publish', to_field='nid')
pub_date = models.DateTimeField(auto_now_add=True)
author = models.ManyToManyField(to='Author', through='Book2Author', through_fields=('book', 'author'))
def __str__(self):
return self.name
class Meta:
verbose_name = '图书表'
verbose_name_plural = verbose_name
class Publish(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
email = models.EmailField()
class Meta:
verbose_name = '出版社表'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Author(models.Model):
nid = models.AutoField(primary_key=True)
name = models.CharField(max_length=32)
phone = models.CharField(max_length=32, default=15764503613)
class Meta:
verbose_name = '作者表'
verbose_name_plural = verbose_name
def __str__(self):
return self.name
class Book2Author(models.Model):
nid = models.AutoField(primary_key=True)
book = models.ForeignKey(to='Book', to_field='nid')
author = models.ForeignKey(to='Author', to_field='nid')
class Meta:
verbose_name = '图书作者表'
verbose_name_plural = verbose_name
四 配置自定义权限类
1. 局部配置
假设在用户认证通过后需要判断用户的权限,那么需要在该视图类中定义一个参数permission_classes
class Book(APIView):
authentication_classes = [authticate_classes.BookAuth]
permission_classes = [permiss_classes.LoginPermission]
# authentication_classes = []
def dispatch(self, request, *args, **kwargs):
return super().dispatch(request, *args, **kwargs)
def get(self, request, id):
print(request.user, '444')
response = {'status': 100, 'msg': None}
book_obj = models.Book.objects.filter(pk=id).first()
if book_obj:
book_ser = myser.BookSer(book_obj, many=False)
response['book'] = book_ser.data
else:
response['msg'] = '图书没有对象'
response['status'] = 101
return Response(response)
2. 全局使用
全局使用的话需要在项目的 settings 中配置,如下:
REST_FRAMEWORK={
'DEFAULT_PERMISSION_CLASSES': ['app01.permiss_classes.LoginPermission']
}
3. 局部禁用
局部禁用需要在视图类中定义一个空的permission_classes
permission_classes = []