一、权限组件

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 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': [
# 默认权限类配置
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 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 = {
# 配置默认权限类
'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, 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(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. 接口测试

只有登录后才能访问
mark

登录用户和游客可读
mark

登录用户有所有权限
mark

二、自定义权限类

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 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, 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(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. 测试接口

有认证信息且正确,有所有权限
mark

无认证信息,或有错误认证信息,无写权限
mark

无认证信息,有读权限
mark