Django-restframework 之許可權原始碼分析
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 = []