rest framework之权限组件
一、权限组件的使用
1、自定义权限
要实现自定义权限,需要重写BasePermission
并实现以下方法中的一个或两个
.has_permission(self, request, view)
.has_object_permission(self, request, view, obj)
如果请求被授予访问权限,方法应该返回True
,否则返回False
。
@six.add_metaclass(BasePermissionMetaclass) class BasePermission(object): """ A base class from which all permission classes should inherit. """ def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ return True def has_object_permission(self, request, view, obj): """ Return `True` if permission is granted, `False` otherwise. """ return True
权限组件需要与认证组件进行配合,因为一个用户只有认证登录,拿到用户信息。此时根据认证后的用户拿到相应的权限进行操作,下面是基于rbac权限控制如何利用
restframework中的权限组件。
from django.conf import settings import re from rest_framework.permissions import BasePermission class Permission(BasePermission): message = "无权限访问" def has_permission(self, request, view): """ Return `True` if permission is granted, `False` otherwise. """ permissions_dict = {} menus_dict = {} """ 从数据库拿到该用户的所有权限, request.user是认证后拿到的元组第一个值 """ permissions_queryset = request.user.roles.filter( permissions__url__isnull=False).values( 'permissions__id', 'permissions__url', 'permissions__title', 'permissions__parent_id', 'permissions__action__code', 'permissions__menu_id', 'permissions__menu__title', 'permissions__menu__icon', 'permissions__menu__position').distinct() """ 将用户的权限进行数据结构的调整 { '/index.html': ['GET','POST','DEL','EDIT], r'/detail-(\d+).html': ['GET','POST','DEL','EDIT], } """ for row in permissions_queryset: if row["permissions__url"] in permissions_dict: permissions_dict[row["permissions__url"]].append( row["permissions__action__code"]) else: permissions_dict[row["permissions__url"]] = [ row["permissions__action__code"], ] """ 初始化用户菜单,也就是该用户可以访问的菜单 """ for row in permissions_queryset: menu_id = row["permissions__menu_id"] if not menu_id: continue if menu_id not in menus_dict: menus_dict[row["permissions__menu__position"]] = { "id": row["permissions__menu_id"], "title": row["permissions__menu__title"], "icon": row["permissions__menu__icon"], "children": [ { 'id': row['permissions__id'], 'title': row['permissions__title'], 'url': row['permissions__url'] } ] } else: menus_dict[row["permissions__menu__position"]]["children"].append( { 'id': row['permissions__id'], 'title': row['permissions__title'], 'url': row['permissions__url'] } ) """ 将该用户的权限赋值给视图,这样在视图中可以拿到对应的权限, 传给前端进行按钮级别的权限验证 """ view.permissions_dict = permissions_dict """ 将该用户的菜单赋值给视图,这样在视图中可以拿到对应的菜单, 传给前端进行左侧菜单的初始化 """ view.menus_dict = menus_dict """ 过滤白名单 """ for pattern in settings.RBAC_NO_AUTH_URL: if re.match(pattern, request.path_info): return None if not permissions_dict: return False # 请求url与用户拥有的权限进行匹配 """ /crm/menus {'/rights': ['get'], '/user': ['get', 'post'], '/roles': ['get']} """ flag = False for pattern, code_list in permissions_dict.items(): upper_code_list = [item.upper() for item in code_list] request_permission_code = request.method if re.match(pattern, request.path_info): if request_permission_code in upper_code_list: permission_code_list = upper_code_list # 将用户角色拥有的请求方式赋值给视图 view.PERMISSION_CODE_LIST_KEY = permission_code_list flag = True break if not flag: return False return True
当认证类完成后,就可以进行全局注册或者局部注册:
- 全局注册
REST_FRAMEWORK = { # 全局使用的认证类 "DEFAULT_AUTHENTICATION_CLASSES":['crm.utils.auth.AuthToken',], #全局权限类 "DEFAULT_PERMISSION_CLASSES": ['rbac.permissions.Permission', ], }
值得注意的是,在全局权限中使用了认证过的用户,所以必须要有认证类,这样才会针对某个用户进行权限的控制,认证相关请参考:https://www.cnblogs.com/shenjianping/p/11387324.html。
其次,登录视图中不需要认证和权限,所以应该免认证和权限验证,只需要在相应的视图中进行配置:
class LoginView(APIView): authentication_classes = [] # 登陆页面免认证 permission_classes = [] #登录免权限 def post(self,request,*args,**kwargs) pass ...
- 局部注册
只需要在每一个视图中进行配置即可:
from rbac.permissions import Permission class UserModelView(GenericViewSet): permission_classes = [Permission,] #局部权限配置 def list(self,request,*args,**kwargs) pass ...
2、内置权限
- AllowAny
允许不受限制的访问,而不管该请求是否已通过身份验证或未经身份验证
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
- IsAuthenticated
拒绝任何未经身份验证的用户的权限,并允许其他权限
class IsAuthenticated(BasePermission): """ Allows access only to authenticated users. """ def has_permission(self, request, view): return bool(request.user and request.user.is_authenticated)
- IsAuthenticatedOrReadOnly
允许经过身份验证的用户执行任何请求。只有当请求方法是“安全”方法(GET
, HEAD
或 OPTIONS
)之一时,才允许未经授权的用户请求
class IsAuthenticatedOrReadOnly(BasePermission): """ The request is authenticated as a user, or is a read-only request. """ def has_permission(self, request, view): return bool( request.method in SAFE_METHODS or request.user and request.user.is_authenticated )
- IsAdminUser
除非user.is_staff
为True
,否则IsAdminUser
权限类将拒绝任何用户的权限,在这种情况下将允许权限
class IsAdminUser(BasePermission): """ Allows access only to admin users. """ def has_permission(self, request, view): return bool(request.user and request.user.is_staff)
二、源码剖析
在restframework中对View类封装成了APIView,而APIView中主要是dispatch方法的不同,在其方法中进行了request的重构以及加入一些组件(认证组件、权限组件等)。
def dispatch(self, request, *args, **kwargs): """ `.dispatch()` is pretty much the same as Django's regular dispatch, but with extra hooks for startup, finalize, and exception handling. """ self.args = args self.kwargs = kwargs #rest-framework重构request对象 request = self.initialize_request(request, *args, **kwargs) self.request = request self.headers = self.default_response_headers # deprecate? try: self.initial(request, *args, **kwargs) # Get the appropriate handler method #这里和CBV一样进行方法的分发 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 response = handler(request, *args, **kwargs) except Exception as exc: response = self.handle_exception(exc) self.response = self.finalize_response(request, response, *args, **kwargs) return self.response
执行self.initial(request, *args, **kwargs)中:
def initial(self, request, *args, **kwargs): """ ... # Ensure that the incoming request is permitted self.perform_authentication(request) #进行认证 self.check_permissions(request) #权限相关 self.check_throttles(request)
执行self.check_permissions(request)中:
def check_permissions(self, request): """ Check if the request should be permitted. Raises an appropriate exception if the request is not permitted. """ for permission in self.get_permissions(): #如果没有权限抛出异常 if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) )
在这里进行权限的检验,循环所有的权限类对象(self.get_permissions())。
def get_permissions(self): """ Instantiates and returns the list of permissions that this view requires. """ return [permission() for permission in self.permission_classes]
所以,可以在视图中局部配置permission_classes这样的权限类的列表,另外循环的每一个权限类中必须实现has_permission方法,如果有权限就返回True,如果没有就返回false,
如果返回False就会执行check_permissions中的permission_denied方法,会抛出一个异常。
def permission_denied(self, request, message=None): """ If request is not permitted, determine what kind of exception to raise. """ if request.authenticators and not request.successful_authenticator: raise exceptions.NotAuthenticated() raise exceptions.PermissionDenied(detail=message)
当然自己也是可以更改与异常关联的错误消息,直接在自定义权限类实现消息属性。
from rest_framework.permissions import BasePermission class Permission(BasePermission): message = "无权限访问" ...
参考文档:https://www.django-rest-framework.org/api-guide/permissions/
原文地址:https://www.cnblogs.com/shenjianping/p/11489166.html
- Android之倒计时CountdownTimer用法
- WriteUp分享 | LCTF的一道padding oracle攻击+sprintf格式化字符串导致的SQL注入
- 10.27 target介绍
- Android 之游戏开发流程
- shell脚本后台运行
- Android的.so文件你需要知道那些知识
- xshel配色方案
- cocos2dx-v3.5 2048 (一): 项目架构
- cocos2dx-v3.5 2048 (二): GameTool的设计与实现
- cocos2dx-v3.5 2048(三):菜单实现
- 2017 LCTF WriteUp 4篇
- cocos2dx-v3.4 2048(四):单元格的设计与实现
- javascript - 闭包
- Java 反射简单实例
- JavaScript 教程
- JavaScript 编辑工具
- JavaScript 与HTML
- JavaScript 与Java
- JavaScript 数据结构
- JavaScript 基本数据类型
- JavaScript 特殊数据类型
- JavaScript 运算符
- JavaScript typeof 运算符
- JavaScript 表达式
- JavaScript 类型转换
- JavaScript 基本语法
- JavaScript 注释
- Javascript 基本处理流程
- Javascript 选择结构
- Javascript if 语句
- Javascript if 语句的嵌套
- Javascript switch 语句
- Javascript 循环结构
- Javascript 循环结构实例
- Javascript 跳转语句
- Javascript 控制语句总结
- Javascript 函数介绍
- Javascript 函数的定义
- Javascript 函数调用
- Javascript 几种特殊的函数
- JavaScript 内置函数简介
- Javascript eval() 函数
- Javascript isFinite() 函数
- Javascript isNaN() 函数
- parseInt() 与 parseFloat()
- escape() 与 unescape()
- Javascript 字符串介绍
- Javascript length属性
- javascript 字符串函数
- Javascript 日期对象简介
- Javascript 日期对象用途
- Date 对象属性和方法
- Javascript 数组是什么
- Javascript 创建数组
- Javascript 数组赋值与取值
- Javascript 数组属性和方法
- SAP Spartacus PagelayoutComponent里的section和slot
- SAP Spartacus ComponentData的提前subscription
- 如何在 SwiftUI 中使用手势
- jQuery 尺寸、位置操作
- SwiftUI:触控反馈
- .net core ef core 自动迁移,自动修改数据库
- 白话 CRC
- pytest封神之路第六步 断言技巧
- pytest封神之路第七步 用例查找原理
- 字符串-KMP
- 数据分析与数据挖掘 - 08图形绘制
- 数据分析与数据挖掘 - 09邻近算法
- 字符串-AC自动机(详细图解)
- 挖洞经验 | Google Play Core Library中的代码执行漏洞
- 设计模式-七大原则(图解一目了然)