一、权限组件介绍 1. 用户权限的作用 在一个系统中,每个用户都应该有一个或多个角色,如普通用户、管理员、会员、超级会员等等,不同的角色有着不同的权限。drf
中提供了权限操作的组件,可以帮助开发者对不同角色进行相应的业务处理。
权限操作的前提是必须知道当前登录用户是谁,因此权限组件往往是和用户认证组件联合使用的。
2. 权限组件的使用 2.1 编写数据表模型 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 from django.db import modelsclass UserInfo (models.Model): role_choice = ((1 , "普通用户" ), (2 , "管理员" ), (3 , "超级管理员" )) role = models.SmallIntegerField(choices=role_choice, verbose_name="用户角色" , default=1 ) name = models.CharField(max_length=10 , verbose_name="用户名" , null=False ) password = models.CharField(max_length=16 , verbose_name="用户密码" , null=False ) token = models.CharField(max_length=32 , verbose_name="用户登录token" , null=True ) def __str__ (self ): return self .name class Meta : verbose_name_plural = "user_info" db_table = verbose_name_plural
2.2 编写权限类 权限类必须继承 rest_framework.permissions.BasePermission
类,且必须实现内部的 has_permission
方法,而 has_object_permission
方法可以根据不同的视图类去实现(也可以不实现)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 from rest_framework.permissions import BasePermissionclass UserPermission (BasePermission ): message = {"code" : 1003 , "error" : "无权访问" } def has_permission (self, request, view ): if request.user.role == 1 : return False return True def has_object_permission (self, request, view, obj ): return True
2.3 编写认证类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedfrom app01.models import UserInfoclass HeardTokenAuth (BaseAuthentication ): """ 从请求头中获取token值进行验证 """ def authenticate (self, request ): token = request.META.get("HTTP_TOKEN" , "" ) if not token: raise AuthenticationFailed({"code" : 1002 , "error" : "认证失败" }) user_obj = UserInfo.objects.filter (token=token).first() if not user_obj: raise AuthenticationFailed({"code" : 1002 , "error" : "认证失败" }) return user_obj, token def authenticate_header (self, request ): return 'Bearer realm="API"'
2.4 路由 1 2 3 4 5 6 7 8 9 10 from django.urls import pathfrom app01 import views urlpatterns = [ path('api/v1/login/' , views.UserLoginView.as_view()), path('api/v1/product/' , views.ProductManage.as_view()), ]
2.5 编写视图类 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 import uuidfrom rest_framework.views import APIViewfrom rest_framework.response import Responsefrom app01.models import UserInfofrom app01.auth import HeardTokenAuthfrom app01.permission import UserPermissionclass UserLoginView (APIView ): authentication_classes = [] def post (self, request, *args, **kwargs ): data = request.data user_name = data.get("username" , "" ) pwd = data.get("password" , "" ) user_obj = UserInfo.objects.filter (name=user_name, password=pwd).first() if not user_obj: ret_data = {"code" : 1001 , "error" : "用户名或密码错误" } return Response(ret_data) user_token = str (uuid.uuid4()) user_obj.token = user_token user_obj.save() ret_data = { "code" : 1000 , "data" : { "user" : user_name, "token" : user_token } } return Response(ret_data)class ProductManage (APIView ): authentication_classes = [HeardTokenAuth, ] permission_classes = [UserPermission, ] def get (self, request, *args, **kwargs ): product_list = [ {"id" : 1 , "title" : "mate30" , "price" : 3000 }, {"id" : 2 , "title" : "mate40" , "price" : 4000 }, {"id" : 3 , "title" : "mate50" , "price" : 5000 }, ] return Response(product_list)
2.6 权限校验 启动服务,使用 post man
进行请求,先访问 http://127.0.0.1:8000/api/v1/login/
进行用户登录获取 token
,访问 http://127.0.0.1:8000/api/v1/product/
时,在请求头中携带 token
。
2.7 权限组件的全局使用 1 2 3 4 5 6 REST_FRAMEWORK = { "DEFAULT_PERMISSION_CLASSES" : ["app01.permission.UserPermission" ] }
二、多个权限类 和认证类一样,视图也可以指定多个权限类,且必须通过所有指定的权限类以后,权限验证才算通过。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 class Role (models.Model): """ 角色表 """ title = models.CharField(max_length=10 , verbose_name="角色名称" , null=False ) def __str__ (self ): return self .title class Meta : verbose_name_plural = "role" db_table = verbose_name_pluralclass User_info (models.Model): """ 用户表 """ name = models.CharField(max_length=10 , verbose_name="用户名" , null=False ) password = models.CharField(max_length=16 , verbose_name="用户密码" , null=False ) token = models.CharField(max_length=32 , verbose_name="用户登录token" , null=True ) role = models.ManyToManyField(to="Role" ) def __str__ (self ): return self .name class Meta : verbose_name_plural = "user_info" db_table = verbose_name_plural
执行数据库迁移命令后,手动给角色表添加三条数据
再分别创建两个用户,其中一个用户绑定管理员和超级管理员两个角色,另一个用户只绑定管理员角色。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 from rest_framework.permissions import BasePermissionclass AdminPermission (BasePermission ): """校验管理员权限""" message = {"code" : 1003 , "error" : "无权访问" } def has_permission (self, request, view ): if request.user.role.filter (title="管理员" ).exists(): return True return False def has_object_permission (self, request, view, obj ): return True class SuperAdminPermission (BasePermission ): """校验超级管理员权限""" message = {"code" : 1003 , "error" : "无权访问" } def has_permission (self, request, view ): if request.user.role.filter (title="超级管理员" ).exists(): return True return False def has_object_permission (self, request, view, obj ): return True
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 class ProductManage (APIView ): authentication_classes = [HeardTokenAuth, ] permission_classes = [AdminPermission, SuperAdminPermission] def get (self, request, *args, **kwargs ): product_list = [ {"id" : 1 , "title" : "mate30" , "price" : 3000 }, {"id" : 2 , "title" : "mate40" , "price" : 4000 }, {"id" : 3 , "title" : "mate50" , "price" : 5000 }, ] return Response(product_list)
三、权限组件源码分析 权限组件功能的实现可以通过源码简单剖析一下
第一步,还是通过视图类先走到 APIView
中的 dispatch
方法,执行里面的 self.initial
;
第二步,self.initial
里调用了一个 check_permissions
方法,权限组件的验证功能就是通过该方法实现的;
第三步,check_permissions
中主要做了以下几件事
调用 get_permissions
方法,将视图类中 permission_classes
中所有的权限类进行实例化,返回一个全是实例化对象的列表(如果视图类中未定义 permission_classes
,则去父类 APIView
中寻找);
循环对象列表,执行每个对象的 has_permission
方法,返回 True 则继续循环,返回 False 则调用 permission_denied
抛弃异常,权限校验流程结束。