針對同一個 view
,對不一樣的接口設置不一樣的權限python
看了 DRF views
源碼:app
def initial(self, request, *args, **kwargs): """ Runs anything that needs to occur prior to calling the method handler. """ self.format_kwarg = self.get_format_suffix(**kwargs) # Perform content negotiation and store the accepted info on the request neg = self.perform_content_negotiation(request) request.accepted_renderer, request.accepted_media_type = neg # Determine the API version, if versioning is in use. version, scheme = self.determine_version(request, *args, **kwargs) request.version, request.versioning_scheme = version, scheme # Ensure that the incoming request is permitted self.perform_authentication(request) self.check_permissions(request) self.check_throttles(request)
經過源碼能夠發現,每次請求進來,都要作認證,權限驗證和限流驗證。若是全部接口都須要權限,這直接在視圖類中直接設置 permission_classes
便可;若是針對業務中部分接口須要權限,其餘不須要權限的場景,這樣一刀切的方式是行不通的,由於進來的請求會被權限打回去,針對部分接口須要權限的場景,可能須要變通一下。ide
例若有個分類模型url
class Category(models.Model): name = models.CharField('名稱', max_length=30) created = models.DateTimeField('生成時間', auto_now_add=True) def __str__(self): return self.name
若是使用 DRF 的 viewsetrest
class CategoryViewSet(viewsets.ModelViewSet): queryset = Category.objects.all() serializer_class = CategorySerializer
DRF 會自動生成六個方法,具體就不說了,對於我的博客來講,獲取列表以及詳情是不須要設置權限的,可是對於更新,建立,刪除時須要作權限驗證的,那麼問題來了,我該怎麼作好權限驗證,這裏我假使獲取詳情須要管理員權限,其餘方法都不須要權限驗證code
寫包裝權限的裝飾器orm
from functools import update_wrapper def wrap_permission(*permissions, validate_permission=True): """custom permissions for special route""" def decorator(func): def wrapper(self, request, *args, **kwargs): self.permission_classes = permissions if validate_permission: self.check_permissions(request) return func(self, request, *args, **kwargs) return update_wrapper(wrapper, func) return decorator
自定義權限類接口
from rest_framework.permissions import IsAdminUser class IsVbAdminUser(IsAdminUser): """ Allows access only to admin users. """ def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return self.has_permission(request, view)
而後在 viewset 中,例如獲取詳情接口,這樣用,ci
@wrap_permission(IsVbAdminUser) def retrieve(self, request, *args, **kwargs):
若是直接請求,就會下面錯誤路由
"error_message": "Authentication credentials were not provided."
後面在 DRF 的裝飾器中找到 permission_classes
def permission_classes(permission_classes): def decorator(func): func.permission_classes = permission_classes return func return decorator
這個默認的業務還須要定製,根據本身業務須要了
@detail_route( url_path='test', permission_classes=(IsVbAdminUser, ), ) def test(self, request, *args, **kwargs):
這樣耍的緣由,在看了 DRF route 源代碼,發現
method_kwargs = getattr(viewset, methodname).kwargs initkwargs = route.initkwargs.copy() initkwargs.update(method_kwargs)
是否是很好玩
對於自定義路由,使用這種方法,仍是蠻方便的。
重寫 initial 方法,根據方法定義不一樣的權限類
添加 permission_classes_map
類屬性
permission_classes_map
定義接口和權限的映射,用法以下:
permission_classes_map = { 方法名: 權限類列表 }
此爲特定接口的權限檢測,例如若是視圖中包含 create 方法,同時在又在視圖中設置了全局性的 permission_classes,
可是想爲 create 定義不一樣於全局的權限,因此這裏能夠這樣設置,示例以下:
permission_classes_map = { 'create': [CustomPermission] }
permission_classes_map = {} def initial(self, request, *args, **kwargs): """從新定義此方法,添加靈活配置權限映射""" if request.method.lower() in self.http_method_names: handler = getattr(self, request.method.lower(), self.http_method_not_allowed) else: handler = self.http_method_not_allowed if hasattr(handler, '__name__'): handler_name = handler.__name__ elif hasattr(handler, '__func__'): handler_name = handler.__func__.__name__ else: handler_name = None if handler_name and handler_name in self.permission_classes_map: if isinstance(self.permission_classes_map.get(handler_name), (tuple, list)): self.permission_classes = self.permission_classes_map.get(handler_name) return super(CommonWithSignViewSet, self).initial(request, *args, **kwargs)
把須要權限驗證的和不須要權限驗證的視圖分開,寫兩個視圖,可是這樣作會冗餘部分代碼
我在以往的經歷中,若是是針對默認的路由須要加權限驗證,我會使用方法 1,對於自定義路由,我會使用方法 2,固然也能夠反過來思考,設置全局的權限驗證,若是那個方法不須要權限驗證,使用裝飾器把權限設置爲空便可,隨便怎麼折騰
前兩種方法儘可能不要再視圖屬性中設置 permission_classes
,這樣的處理有點奇葩,我的能夠針對 DRF 處理流程,進行包裝,看業務須要。