Django Rest Framework之权限
基本代码结构
url.py:
from django.conf.urls import url, include from app import views urlpatterns = [ url(r'^test/', views.TestView.as_view()), ]
views.py:
from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.request import Request from rest_framework import exceptions class MyPermission(object): def has_permission(request, self): ''' 权限代码编写区域 ''' return True #权限通过 如果权限不通过 返回False class TestView(APIView): permission_classes = [MyPermission, ] def get(self, request, *args, **kwargs): pass def post(self, request, *args, **kwargs): pass ''' 等等一系列的视图功能方法 '''
说明:
-
- has_permission方法的返回值是布尔类型,True表示权限通过,False表示权限拒绝
- 上面的基本结构是做局部的类的权限判断方式,全局权限判断后文介绍。
- permission_classes属性变量同样也是一个列表,列表元素是权限判断类。
源码分析
其实权限的源码流程跟认证的流程基本相同。还是要抓住通过源码要想知道什么,不然就会陷入浩如烟海的源码之中。
-
为什么会使用permission_classes属性变量?
python 的面向对象编程中,我们首先要执行的方法肯定是dispatch方法,所以我们的分析入口就是dispatch方法,在dispatch方法中,可以看到,通过initialize_request方法将django原生的request进行了一次封装。由initialize_request方法的实现过程可以看出,将其封装实例化成了一个Request对象。但权限判断并没有像认证一样初始化到了Request对象中,但对django原生的request封装还是需要强调的,因为编写代码的过程中对django原生的request的使用是必不可免的。
同样的,权限判断的具体过程跟认证一样,也是在dispatch方法中所调用的initial方法中实现。再跳转到initial方法中去。
在initial方法中,可以看到权限判断的方法,没错,就是通过check_permissions方法实现的。再跳转到这个方法中去。
在check_permissions方法中,就可以看到权限的判断就是通过这个for循环实现的。正因为在业务代码中可能存在若干种类型的权限判断,所以才会通过循环去执行我们定义好的权限判断类来完成多个权限体系的判断功能。这样,我们可以感觉到这里的“self.get_permissions()”的返回值应该就是我们在视图类中赋值过的permissions_classes属性变量的值。那就跳转到这个方法中去看看吧。
在get_permissions方法中看到,跟认证一样,返回值同样是一个列表生成式,而这个列表生成式使用的属性变量正是我们赋值过的permission_classes,跟我们之前的猜测完全一致。综上所述,我们为了让drf接口源码使用上我们自己定义的权限判断类,那我们就必须按照源码中写的借口,将permission_classes属性变量赋值
-
在权限判断类中为什么会定义一个名称为has_permission的方法?
回到check_permissions方法中,我们看if判断句,前面刚刚说过,在for中的permission其实就是我们自己定义的权限判断类,那么在if句中的“.has_permission(request,self)”不就应该就是Mypermission类中的方法吗?所以,我们自己定义的Mypermission类中一定要实现has_permission这个方法。(要注意这个方法的参数)
-
has_permission方法中,为什么返回值为布尔值?
还是跟上一个问题一样的,在上图中的if句中,我们可以看到“permission.has_permission(request, self)”的返回值不就是布尔值吗,这个返回值不就是has_permission方法返回值吗?当返回值为False时,就会执行if句中的代码,来抛出异常。
实例
from django.conf.urls import url, include from web.views import TestView urlpatterns = [ url(r'^test/', TestView.as_view()), ]
#!/usr/bin/env python # -*- coding:utf-8 -*- from rest_framework.views import APIView from rest_framework.response import Response from rest_framework.authentication import BaseAuthentication from rest_framework.permissions import BasePermission from rest_framework.request import Request from rest_framework import exceptions token_list = [ 'sfsfss123kuf3j123', 'asijnfowerkkf9812', ] class TestAuthentication(BaseAuthentication): def authenticate(self, request): """ 用户认证,如果验证成功后返回元组: (用户,用户Token) :param request: :return: None,表示跳过该验证; 如果跳过了所有认证,默认用户和Token和使用配置文件进行设置 self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() # 默认值为:匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN()# 默认值为:None else: self.auth = None (user,token)表示验证通过并设置用户名和Token; AuthenticationFailed异常 """ val = request.query_params.get('token') if val not in token_list: raise exceptions.AuthenticationFailed("用户认证失败") return ('登录用户', '用户token') def authenticate_header(self, request): """ Return a string to be used as the value of the `WWW-Authenticate` header in a `401 Unauthenticated` response, or `None` if the authentication scheme should return `403 Permission Denied` responses. """ pass class TestPermission(BasePermission): message = "权限验证失败" def has_permission(self, request, view): """ 判断是否有权限访问当前请求 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :return: True有权限;False无权限 """ if request.user == "管理员": return True # GenericAPIView中get_object时调用 def has_object_permission(self, request, view, obj): """ 视图继承GenericAPIView,并在其中使用get_object时获取对象时,触发单独对象权限验证 Return `True` if permission is granted, `False` otherwise. :param request: :param view: :param obj: :return: True有权限;False无权限 """ if request.user == "管理员": return True class TestView(APIView): # 认证的动作是由request.user触发 authentication_classes = [TestAuthentication, ] # 权限 # 循环执行所有的权限 permission_classes = [TestPermission, ] def get(self, request, *args, **kwargs): # self.dispatch print(request.user) print(request.auth) return Response('GET请求,响应内容') def post(self, request, *args, **kwargs): return Response('POST请求,响应内容') def put(self, request, *args, **kwargs): return Response('PUT请求,响应内容')
扩展:全局权限
同样,跟全局认证一样,我们只需要在settings配置文件中添加配置项即可。然后,我们仍然需要将我们自定义的权限类也写到我们在跟views.py同级目录下新建的文件夹(我习惯叫utils)中的权限判断文件(permision.py)中去。
REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES" :['api.utils.permission.Mypermission',] }
Mypermission就是我们写在utils文件夹中permission.py文件中的一个权限类。
注意:如果有部分类不需要权限判断的话,可以在Mypermission类中添加“permission_classes = []”,即可。