43.Permission源码解析和自定义权限类
- 认证、限流,权限决定是否应该接收请求或拒绝访问
- 权限检查在视图的最开始处执行,在继续执行其他代码前
- 权限检查通常会使用request.user和request.auth属性中的身份认证信息来决定是否允许请求
- 不同级别的用户访问不同的api过程中,使用权限来控制访问的许可
- DRF框架的权限被定义为一个权限类的列表,表示拥有列表中所有类型的权限
- 在运行视图代码主体之前,会检查列表中的每个权限
- 任何一个权限检查失败的话,会抛出 exceptions.PermissionDenied 或 exceptions.NotAuthenticated 异常,视图主体代码不会运行
- 权限检查失败时,会返回403 Forbidden或401 Unauthorized响应
- 响应规则如下:
- 请求已成功通过身份验证,但不具备访问权限,返回403 Forbidden响应。
- 请求未通过身份认证,并且最高优先级的认证类未使用 WWW-Authenticate 标头, 返回403 Forbidden响应。
- 请求未通过身份认证,但是最高优先级的认证类使用了 WWW-Authenticate 标头,返回HTTP 401未经授权的响应,并附带适当的WWW-Authenticate报头
#settings全局权限配置
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES':(
#drf默认的权限,允许任何访问
'rest_framework.permissions.AllowAny',
)
}
# 局部视图级别的权限配置
class UserInfo(ModelViewSet):
# 列表传入要设置的权限
permission_classses = []
'''
全局权限和局部权限都有配置,以局部配置为准,装饰器请求的权限设置同样
'''
class BasePermission(metaclass=BasePermissionMetaclass):
def has_permission(self, request, view):
return True
def has_object_permission(self, request, view, obj):
return True
class AllowAny(BasePermission):
def has_permission(self, request, view):
return True
class IsAuthenticated(BasePermission):
def has_permission(self, request, view):
# request.user,如果有值,说明当前有用户,判断用户是否登录
# request.user.is_authenticated,判断用户是否通过认证
# 如果两个都通过,bool返回True,可以后续执行,否则False不可执行
return bool(request.user and request.user.is_authenticated)
class IsAdminUser(BasePermission)
def has_permission(self, request, view):
# request.user,如果有值,说明当前有用户,判断用户是否登录
# request.user.is_staff属性Django 的auth的属性,如果是True,说明是管理员
# 如果两个都通过,bool返回True,可以后续执行,否则False不可执行
return bool(request.user and request.user.is_staff)
class IsAuthenticatedOrReadOnly(BasePermission):
def has_permission(self, request, view):
return bool(
# SAFE_METHODS是定义的安全方法list,GET、HEAD、OPTIONS三种请求方式
# 如果请求方式是三种安全方式之一,直接可以访问,三种读取请求
# 或者 request.user and request.user.is_authenticated,判断是否登录以及是否验证通过
# 两种条件满足一种,如果为True,只读或者读写,否则拒绝访问
request.method in SAFE_METHODS or
request.user and
request.user.is_authenticated
)
class DjangoModelPermissions(BasePermission):
#权限类属性设定
perms_map = {
'GET': [], # 空list,不需要具有模型权限
'OPTIONS': [],# 空list,不需要具有模型权限
'HEAD': [], # 空list,不需要具有模型权限
'POST': ['%(app_label)s.add_%(model_name)s'], # POST 请求需要用户对 add 模型具有权限。
'PUT': ['%(app_label)s.change_%(model_name)s'], #PUT请求要求用户对 change 模型具有权限。
'PATCH': ['%(app_label)s.change_%(model_name)s'],#POST请求要求用户对 change 模型具有权限。
'DELETE': ['%(app_label)s.delete_%(model_name)s'], #DELETE 请求需要用户对 delete 模型具有权限
}
authenticated_users_only = True
def get_required_permissions(self, method, model_cls):
kwargs = { # 获取请求app_label和model_name
'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.model_name
}
# 请求方式不在设定的类属性中则抛出异常
if method not in self.perms_map:
raise exceptions.MethodNotAllowed(method)
# self.perms_map[method]获取指定请求方式的值,拼接kwargs
return [perm % kwargs for perm in self.perms_map[method]]
def _queryset(self, view):
#断言 view是否有get_queryset方法或者获取queryset指定的值
assert hasattr(view, 'get_queryset') \
or getattr(view, 'queryset', None) is not None, (
'Cannot apply {} on a view that does not set '
'`.queryset` or have a `.get_queryset()` method.'
).format(self.__class__.__name__)
# 如果view有getqueryset_set
if hasattr(view, 'get_queryset'):
queryset = view.get_queryset()
# queryset赋值view的get_queryset()方法
# 如果不为空返回 queryset
assert queryset is not None, (
'{}.get_queryset() returned None'.format(view.__class__.__name__)
)
return queryset
return view.queryset
def has_permission(self, request, view):
# 如果 ignore_model_permissions有值,返回True
if getattr(view, '_ignore_model_permissions', False):
return True
# 如果没有登录或者 没有认证属性返回False
if not request.user or (
not request.user.is_authenticated and self.authenticated_users_only):
return False
queryset = self._queryset(view)
# 通过self.get_required_permissions设定对应权限
perms = self.get_required_permissions(request.method, queryset.model)
return request.user.has_perms(perms)
'''
也可以通过自定义模型权限,重写以上的默认行为
自定义模型权限,请覆盖 DjangoModelPermissions 并设置 .perms_map 属性
在重写了 get_queryset() 方法的视图中使用此权限,有可能这个视图上却没有queryset 属性。在这种情况下,建议使用保护性的查询集来标记视图,以便确定所需的权限
queryset = User.objects.none()
'''
class DjangoModelPermissionsOrAnonReadOnly(DjangoModelPermissions):
authenticated_users_only = False
- 与 DjangoModelPermissions 一样,此权限只能应用于具有 .queryset 属性或 .get_queryset() 方法的视图,只有在用户通过身份验证并且具有相关的每个对象权限 和相关的模型权限 后,才会被授予此权限
- POST 请求要求用户对模型实例具有 add 权限。
- PUT 和 PATCH 请求要求用户对模型示例具有 change 权限。
- DELETE 请求要求用户对模型示例具有 delete 权限
- 和 DjangoModelPermissions 一样,可以通过重写 DjangoObjectPermissions 并设置 .perms_map 属性来使用自定义模型权限
class DjangoObjectPermissions(DjangoModelPermissions):
perms_map = {
'GET': [],
'OPTIONS': [],
'HEAD': [],
'POST': ['%(app_label)s.add_%(model_name)s'],
'PUT': ['%(app_label)s.change_%(model_name)s'],
'PATCH': ['%(app_label)s.change_%(model_name)s'],
'DELETE': ['%(app_label)s.delete_%(model_name)s'],
}
def get_required_object_permissions(self, method, model_cls):
kwargs = { # 获取app_label和model_name
'app_label': model_cls._meta.app_label,
'model_name': model_cls._meta.model_name
}
# 如果请求方式不在类属性定义中,抛出异常
if method not in self.perms_map:
raise exceptions.MethodNotAllowed(method)
# 返回对应权限属性
return [perm % kwargs for perm in self.perms_map[method]]
def has_object_permission(self, request, view, obj):
# 获取queryset、model以及当前user
queryset = self._queryset(view)
model_cls = queryset.model
user = request.user
# 获取返回的权限属性
perms = self.get_required_object_permissions(request.method, model_cls)
# 如果返回的权限属性有任何一个为False
if not user.has_perms(perms, obj):
# 如果不是安全请求中的method,抛出404异常
if request.method in SAFE_METHODS:
raise Http404
# 获取只读的请求,如果没有只读请求抛出404异常
read_perms = self.get_required_object_permissions('GET', model_cls)
if not user.has_perms(read_perms, obj):
raise Http404
return False
return True
- 继承BasePermission权限基类,根据实现需要编写对应的基类方法
- has_permission(self,request,view)
- view针对哪个视图进行权限检查
- 返回值True,通过权限检查
- 返回值False,没有通过权限检查
- has_object_permission(self,request,view,obj)
- 针对单独的对象进行权限检查
- 如果视图不是继承了通用视图,需要显示的调用check_object_permissions(request,obj)
- 如果继承了通用视图但是重写了get_object(),也需要显示的调用check_object_permissions(request,obj)
- 因为在通用视图中自动调用了check_object_permissions,而非通用视图没有自动调用
- 可以根据是否安全请求返回不同的权限,可以直接判断请求是否在permissions.SAFE_METHODS
- message,权限提示的异常信息,可根据需要决定是否要自定义
# 简单示例
from rest_framework import permissions
# 视图级别权限
class BlackListPermission(permissions.BasePermission):
# 自定义权限提示信息
message = '木子七是黑名单'
def has_permission(self, request, view):
# 如果登录名是木子七 返回False,不是木子七 返回True
return not request.user.username == '木子七'
# 对象级别权限
class IsOwnerOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
# 如果是安全请求直接返回True
if request.method in permissions.SAFE_METHODS:
return True
# 判断操作对象的用户名 是否和登录的用户名一致
return obj.user.username == request.user.username
43.Permission源码解析和自定义权限类的更多相关文章
- rest-framework源码解析和自定义组件----版本
版本 url中通过GET传参自定义的版本 12345678910111213141516171819202122 from django.http import HttpResponsefrom dj ...
- SpringBoot源码学习1——SpringBoot自动装配源码解析+Spring如何处理配置类的
系列文章目录和关于我 一丶什么是SpringBoot自动装配 SpringBoot通过SPI的机制,在我们程序员引入一些starter之后,扫描外部引用 jar 包中的META-INF/spring. ...
- Mybatis源码解析(三) —— Mapper代理类的生成
Mybatis源码解析(三) -- Mapper代理类的生成 在本系列第一篇文章已经讲述过在Mybatis-Spring项目中,是通过 MapperFactoryBean 的 getObject( ...
- admin源码解析及自定义stark组件
admin源码解析 单例模式 单例模式(Singleton Pattern)是一种常用的软件设计模式,该模式的主要目的是确保某一个类只有一个实例存在.当你希望在整个系统中,某个类只能出现一个实例时,单 ...
- Spring Boot @Enable*注解源码解析及自定义@Enable*
Spring Boot 一个重要的特点就是自动配置,约定大于配置,几乎所有组件使用其本身约定好的默认配置就可以使用,大大减轻配置的麻烦.其实现自动配置一个方式就是使用@Enable*注解,见其名知 ...
- Django(63)drf权限源码分析与自定义权限
前言 上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析 源码入口:APIView.py文件下的initial方法下的check_per ...
- Java集合:LinkedList源码解析
Java集合---LinkedList源码解析 一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据re ...
- Java集合---LinkedList源码解析
一.源码解析1. LinkedList类定义2.LinkedList数据结构原理3.私有属性4.构造方法5.元素添加add()及原理6.删除数据remove()7.数据获取get()8.数据复制clo ...
- 深入浅出ReentrantLock源码解析
ReentrantLock不但是可重入锁,而且还是公平或非公平锁,在工作中会经常使用到,将自己对这两种锁的理解记录下来,希望对大家有帮助. 前提条件 在理解ReentrantLock时需要具备一些基本 ...
随机推荐
- DolphinScheduler 功能开发:⼯作流级别任务空跑(后端),测试工作流是否正确执行...
点击上方 蓝字关注我们 ✎ 编 者 按 在今年由中国科学院软件研究所主办的开源软件所供应链点亮计划-开源之夏活动中,有不少小伙伴提交了关于 DolphinScheduler 的项目,本期是来自成都信息 ...
- 映射问题,命名空间不能为空:org.apache.ibatis.builder.BuilderException : Mapper's namesapce cannot be empty
今天配置Spring配置文件时,出现了以下的报错 倒数第三行,意思是Mapper的namespace(命名空间)不能为空 检查xml文件里映射文件是否配置,如果没有配置,那请添加映射文件,不然Spri ...
- k8s驱逐篇(2)-kubelet节点压力驱逐
kubelet节点压力驱逐 kubelet监控集群节点的 CPU.内存.磁盘空间和文件系统的inode 等资源,根据kubelet启动参数中的驱逐策略配置,当这些资源中的一个或者多个达到特定的消耗水平 ...
- leetcode之二叉树
专题:二叉树遍历 987. 二叉树的垂序遍历 给你二叉树的根结点 root ,请你设计算法计算二叉树的 垂序遍历 序列. 对位于 (row, col) 的每个结点而言,其左右子结点分别位于 (row ...
- Express 设置请求跨域
import cors from "cors"; import express from "express"; const app = express(); a ...
- feign远程调用出错
如果你传递的参数,比较复杂时,默认会采用POST的请求方式. 传递单个参数时,推荐使用@PathVariable,如果传递的单个参数比较多,这里也可以采用@RequestParam,Feign接口中不 ...
- 使用JMeter测试.Net5.0,.Net6.0框架下无数据处理的并发情况
1. 安装JMeter及使用 1.1下载JMeter 登录官方网站找到下载链接进行下载:https://jmeter.apache.org/download_jmeter.cgi 1.2配置环境变 ...
- apk编辑器测评
hi你好,我今天要介绍的就是apk编辑器 这里我用的是apk编辑器专业版 APK编辑器 关于 APK 编辑器智友汉化组论坛:bbs.zhiyoo.com修改应用程序名称美化 UI: 更改背景图片删除广 ...
- Python条件语句的用法
python条件语句使用 if 表达式,难度不高,需要注意的是嵌套用法,以及如何设置对应的条件. if 条件判断语句 python 语句是按固定顺序执行的,先执行前面的语句,再执行后面的语句.如果你像 ...
- 给定字符串定义char *a = “I love China!”,读入整数n,输出在进行了a = a + n这个赋值操作以后字符指针a对应的字符串
include<stdio.h> include<string.h> int main() { const char *a="I love China!"; ...