欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Django REST 框架详解 09 | 权限组件

程序员文章站 2022-04-25 15:49:43
...

一、权限组件

1. 分析源码

通过分析源码了解权限组件的方法调用过程

APIView 的 dispatch 中使用 initial 方法实现初始化并进行三大认证,第二步进行权限组件调用

rest_framework/views.py

class APIView(View):
    # ...
    # 定义默认权限类
    permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
    def initial(self, request, *args, **kwargs):
        # ...
        # 认证组件:校验用户
        # 这里调用 perform_authentication 实现认证
        self.perform_authentication(request)
        # 权限组件:校验用户权限
        self.check_permissions(request)
        # 频率组件:限制视图接口被访问次数
        self.check_throttles(request)    
    
    # 权限认证
    def check_permissions(self, request):        
       # 遍历权限对象列表得到一堆权限器,进行权限认证
        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)
                )
                
     # 获取权限
     def get_permissions(self):
        # 由权限类定义
        return [permission() for permission in self.permission_classes]

在 drf 设置文件查看默认权限配置

rest_framework/settings.py

# 默认权限类配置
DEFAULTS = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

查看默认系统权限的实现

rest_framework/permissions.py

class AllowAny(BasePermission):
    """
    Allow any access.
    This isn't strictly required, since you could use an empty
    permission_classes list, but it's useful because it makes the intention
    more explicit.
    """
	# 游客与登录用户都拥有所有权限
    def has_permission(self, request, view):
        return True
    
    class IsAuthenticated(BasePermission):
    """
    Allows access only to authenticated users.
    """

    def has_permission(self, request, view):
        # 只有合法用户有权限,游客无任何权限:
        # 有值且认证通过
        return bool(request.user and request.user.is_authenticated)
	
class IsAdminUser(BasePermission):
	# is_staff:后台管理用户
    # 后台管理用户由所有权限,游客无权限
    def has_permission(self, request, view):
        return bool(request.user and request.user.is_staff)
    
class IsAuthenticatedOrReadOnly(BasePermission):
    def has_permission(self, request, view):
        return bool(
            # 如果是读请求,不校验用户,直接返回
            request.method in SAFE_METHODS or
            # 登录用户有所有权限,游客只读
            request.user and
            request.user.is_authenticated
        )

总结以上的系统权限类:

  • AllowAny:游客与登录用户有所有权限

  • IsAuthenticated:登录用户有所有权限,游客无权限

  • IsAdminUser:后台管理用户由所有权限,游客无权限

  • IsAuthenticatedOrReadOnly:登录用户有所有权限,游客只读

2. 全局配置权限

settings.py

# 全局局部配置
REST_FRAMEWORK = {
    # 配置默认权限类
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.AllowAny',
    ]
}

3. 局部配置权限

views.py

from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet, ViewSet

from utils.response import APIResponse

# 只有登录后才能访问
# 这里认证用的是之前的 MyAuthentication
class AuthenticatedAPIView(APIView):
    permission_classes = [IsAuthenticated]

    def get(self, request, *args, **kwargs):
        return APIResponse(0, 'Authenticated successful')

# 游客只读,用户无限制
class AuthenticatedOrReadOnlyAPIView(APIView):
    permission_classes = [IsAuthenticatedOrReadOnly]

    def get(self, request, *args, **kwargs):
        return APIResponse(0, 'Read successful')

    def post(self, request, *args, **kwargs):
        return APIResponse(0, 'All successful')

urls.py

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^auth/$', views.AuthenticatedAPIView.as_view()),
    url(r'^auth2/$', views.AuthenticatedOrReadOnlyAPIView.as_view()),
]

4. 接口测试

只有登录后才能访问
Django REST 框架详解 09 | 权限组件

登录用户和游客可读
Django REST 框架详解 09 | 权限组件

登录用户有所有权限
Django REST 框架详解 09 | 权限组件

二、自定义权限类

1. 代码实现

  • 继承 BasePermission

  • 重写 has_permission 方法

  • 实现根据自定义权限规则,确定是否有权限

  • 认证规则:

    •   满足设置的用户条件,代表有权限,返回 True
      
    •   满足设置的用户条件,代表有权限,返回 False
      
  • 进行全局或局部配置

    • 全局:配置文件 settings.py
    • 局部:在视图类 import
  • 测试接口:前台在请求头携带认证信息,且默认规范用 Authorization 字段携带认证信息

自定义权限类permissions.py

from rest_framework.permissions import BasePermission, SAFE_METHODS
from django.contrib.auth.models import Group

class MyPermission(BasePermission):

    def has_permission(self, request, view):
        # values_list(falt=True) 获取列表转为集合,与目标求交集
        group = Group.objects.filter(name='administrator').first()
        groups = request.user.groups.all()
        return bool(
            request.method in ('GET', 'HEAD', 'OPTIONS') or
            group and groups and
            group in groups
        )

views.py

from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from rest_framework.views import APIView
from rest_framework.generics import GenericAPIView
from rest_framework.viewsets import GenericViewSet, ViewSet

from api.permissions import MyPermission
from utils.response import APIResponse

# 游客和登录用户只读,登录用户属于管理员分组无限制
class AdminOrReadOnlyAPIView(APIView):
    permission_classes = [MyPermission]

    def get(self, request, *args, **kwargs):
        return APIResponse(0, 'Mypermission Read successful')

    def post(self, request, *args, **kwargs):
        return APIResponse(0, 'Mypermission All successful')

urls.py

from django.conf.urls import url
from api import views

urlpatterns = [
    url(r'^login/$', views.LoginView.as_view()),
    url(r'^auth/$', views.AuthenticatedAPIView.as_view()),
    url(r'^auth2/$', views.AuthenticatedOrReadOnlyAPIView.as_view()),
    url(r'^auth3/$', views.AdminOrReadOnlyAPIView.as_view()),
]

2. 测试接口

有认证信息且正确,有所有权限
Django REST 框架详解 09 | 权限组件

无认证信息,或有错误认证信息,无写权限
Django REST 框架详解 09 | 权限组件

无认证信息,有读权限
Django REST 框架详解 09 | 权限组件