django内置权限系统源码解读

author author     2023-02-07     699

关键词:

Django内置权限系统源码解读_django

前言

之前有篇文章 ​​Django自定义认证系统原理及源码分析解读​​ 带大家分析解读了Django的认证逻辑,而且我们也知道认证是基础,认证通过之后,用户登录到系统,能看到那些,能操作那些,这些都是有​​权限控制​​ 的。

那么本篇就带领大家了解下 Django的权限系统

默认权限

Django在创建模型之后,默认会为每个模型提供​​增删改查​​ 四个权限。默认的权限信息可以在​​auth_permission​​ 表中查看到

sqlite> select * from auth_permission ;
1|1|add_logentry|Can add log entry
2|1|change_logentry|Can change log entry
3|1|delete_logentry|Can delete log entry
4|1|view_logentry|Can view log entry
5|2|add_permission|Can add permission
6|2|change_permission|Can change permission
7|2|delete_permission|Can delete permission
8|2|view_permission|Can view permission
9|3|add_group|Can add group
10|3|change_group|Can change group
11|3|delete_group|Can delete group
12|3|view_group|Can view group
13|4|add_user|Can add user
14|4|change_user|Can change user
15|4|delete_user|Can delete user
16|4|view_user|Can view user
... ...

这个默认权限是怎么创建的呢?

1、我们知道模型创建之后,最终需要通过 ​​python manage.py migrate​​ 在数据库中生效。

所以首先要看 ​​migrate django命令​​的源码

而且如果是自定义Model对应的命令的话,也是在模型APP目录下创建 commands 同名目录,然后创建命令,且是以命令命名文件名称, 文件内容是 class Command(BaseCommand) 类的定义

# django/core/management/commands/migrate.py

from django.core.management.sql import (
emit_post_migrate_signal, emit_pre_migrate_signal,
)

class Command(BaseCommand):
help = "Updates database schema. Manages both apps with migrations and those without."
requires_system_checks = []
... ...
@no_translations
def handle(self, *args, **options):
database = options[database]
... ...
emit_post_migrate_signal(
self.verbosity, self.interactive, connection.alias, apps=post_migrate_apps, plan=plan,
)

从这里得知,在migrate的最后调用了 ​​emit_post_migrate_signal​​ 信号

2、再查看emit_post_migrate_signal信号源码

# django/core/management/sql.py

def emit_post_migrate_signal(verbosity, interactive, db, **kwargs):
# Emit the post_migrate signal for every application.
for app_config in apps.get_app_configs():
if app_config.models_module is None:
continue
if verbosity >= 2:
print("Running post-migrate handlers for application %s" % app_config.label)
models.signals.post_migrate.send(
sender=app_config,
app_config=app_config,
verbosity=verbosity,
interactive=interactive,
using=db,
**kwargs
)

发现它调用了 ​​models.signals.post_migrate​​ 信号,如果了解过Django的信号机制,那么就知道 Django默认定义了一些内置模型相关的信号,都是在 ​​django/db/models/signals.py​

3、而Django默认的User、Permission模型等都是存在于 auth APP下,所以查看对应的源码

# django/contrib/auth/apps.py

from django.db.models.signals import post_migrate

class AuthConfig(AppConfig):
default_auto_field = django.db.models.AutoField
name = django.contrib.auth
verbose_name = _("Authentication and Authorization")

def ready(self):
post_migrate.connect(
create_permissions,
dispatch_uid="django.contrib.auth.management.create_permissions"
)
... ...

发现在auth ​​apps.py​​ 中就调用了 ​​post_migrate​​ 信号机制去调用​​create_permissions​​ 函数

4、查看create_permissions 函数源码

# django/contrib/auth/management/__init__.py

from django.contrib.contenttypes.management import create_contenttypes


def create_permissions(app_config, verbosity=2, interactive=True, using=DEFAULT_DB_ALIAS, apps=global_apps, **kwargs):
if not app_config.models_module:
return

# Ensure that contenttypes are created for this app. Needed if
# django.contrib.auth is in INSTALLED_APPS before
# django.contrib.contenttypes.
create_contenttypes(app_config, verbosity=verbosity, interactive=interactive, using=using, apps=apps, **kwargs)

app_label = app_config.label
... ...

发现在 create_permissions 函数,先会调用 create_contentypes 函数创建对应的 contenttype 然后在创建permissions 。

最终的结果就是看到在 ​​auth_permissions​​ 表中的记录,注意改变content_type 是关联到 ​​auto_content_type​​ 的,所以是先创建 content_type再创建permission

扩展

这里扩展简单说下Django的信号机制

Django的信号机制不同于Linux的信号机制,Django 中的信号用于在框架执行操作时解耦。当某些动作发生的时候,系统会根据信号定义的函数执行相应的操作

Django的信号主要包含以下三个要素:

  • 发送者(sender):信号的发出方。
  • 信号(signal):发送的信号本身。
  • 接收者(receiver):信号的接收者。

其中接受者就是回调函数,会把这个函数注册到信号之上。当特定事件发生之后,发送者发送信号,然后执行回调函数。

Django通过信号的​​connect()​​ 函数监听发送者发送的信号,进行回调函数的处理。如果connect中没有明确指出具体的sender,那么它就监听所有sender

connect() 函数的源码

# django/dispatch/dispatcher.py

class Signal:
... ...
def connect(self, receiver, sender=None, weak=True, dispatch_uid=None):
"""
Connect receiver to sender for signal.
sender
The sender to which the receiver should respond. Must either be
a Python object, or None to receive events from any sender.

核心关注 ​​or None to receive events from any sender.​​ 而 post_migrate 信号注册 create_permissions 回调函数的源代码在 ​​django/contrib/auth/apps.py​​ 具体详见​​上面第三步​​分析

相关的 ​​django监听信号文档​​ 官方文档参考这里, 另外个人还有另外整理的一篇关于 ​​Django的信号机制解读​​ 欢迎阅读

模型自定义权限

这里说的模型自定义权限,是指的Django内置的权限的自定义

权限定义很简单,主要是在模型的Meta属性中,配置 ​​permissions​​ 属性值,是一个列表,每个元素是一个二元组,

class Post(models.Model):
name = models.CharField(max_length=32)
def __str__(self):
return self.name

class Meta:
permissions = [(can_export_posts, can export posts)]

然后执行了migrate之后,就会在系统的 ​​auth_permission​​ 表中新增一条记录

-- 模型默认的 增删改查4个权限
41|11|add_post|Can add post
42|11|change_post|Can change post
43|11|delete_post|Can delete post
44|11|view_post|Can view post
-- 新增的权限记录
45|11|can_export_posts|can export posts

1、判断用户权限

>>> from django.contrib.auth.models import User
>>> user = User.objects.filter(username=james).first()
# 获取用户的所有权限
>>> user.get_all_permissions()
demoapp.add_post, demoapp.view_post

# 查看用户都有哪些查看权限的方法 (输入 user.has_ 之后tab 键)
>>> user.has_
user.has_module_perms( user.has_perm( user.has_perms( user.has_usable_password(

# 查看用户是否具有 某个权限
>>> user.has_perm(demoapp.add_post)
True
>>> user.has_perm(demoapp.delete_post)
False

# 这里了解 has_module_perms()的方法是为了后续讲解 admin后台权限按钮时做准备
#
>>> user.has_module_perms()
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: has_module_perms() missing 1 required positional argument: app_label
>>> user.has_module_perms(demoapp)
True

# has_perms 检查用户是否具有一组权限,给定列表要的只要有一个没有权限就返回False
>>> user.has_perms()
Traceback (most recent call last):
File "<console>", line 1, in <module>
TypeError: has_perms() missing 1 required positional argument: perm_list
>>> user.has_perms([demoapp.add_post])
True
>>> user.has_perms([demoapp.add_post, demoapp.view_post])
True
>>> user.has_perms([demoapp.add_post, demoapp.view_post, demoapp.delete_post])
False

2、新增用户权限

from django.contrib.auth.models import User, Permission

# 获取权限
add_post = Permission.objects.get(codename=add_post)
view_post = Permission.objects.get(codename=view_post)
change_post = Permission.objects.get(codename=change_post)

# 将user的权限设置为当前给定权限值,但是之前权限的会自动去掉
user.user_permission.set([add_post])

# 给用户 user 在当前的权限基础上新增权限
user.user_permission.add(view_post)
user.user_permission.add(view_post, change_post)

# 删除给定的权限
user.user_permission.remove(change_post)

# 清空所有权限
user.user_permission.clear()

3、用户权限组操作

阅读过Django的 auth.models 的源码就知道, Group 模型的permissions属性 关联Permission模型,而User模型有group属性

所以也可以通过 给​​组​​ 分配​​权限​​,然后给用户分配​​组​​ 来实现给用户​​一次性分配一组权限​

具体的实操演示大家可以自行完成,类似与上面的直接给用户分配权限

需要额外说明的, user.get_all_permissions() 其实就是 user.get_user_permissions() + user.get_group_permissions() 的合集

# django/contrib/auth/models.py

class PermissionMixin(models.Model):
... ...
def get_user_permissions(self, obj=None):
return _user_get_permissions(self, obj, user)

def get_group_permissions(self, obj=None):
return _user_get_permissions(self, obj, group)

def get_all_permissions(self, obj=None):
return _user_get_permissions(self, obj, all)

扩展说明

扩展1、关于 has_module_perms 源码, 用户对象具有模型的任意一个权限,那么就返回True

# django/contrib/auth/backends.py

class ModelBackend(BaseBackend):
... ...
def has_module_perms(self, user_obj, app_label):
"""
Return True if user_obj has any permissions in the given app_label.
"""
return user_obj.is_active and any(
perm[:perm.index(.)] == app_label
for perm in self.get_all_permissions(user_obj)
)

扩展2、”权限“ 实质上只是一个"描述符", 告知你具有什么权限而已;然后根据”权限(描述符)“ 去判断用户能否去执行那些”动作(action)“

Django 自带的管理后台,我们知道需要管理自定义的APP种的模型的时候,需要”注册“ 对应的模型到 "admin" 中去

# demoapp/apps.py
from django.contrib import admin
from .models import Post

admin.site.register(Post)

这里的 admin 最终指向的是 ​​AdminSite​

1、首先根据 ​​from django.contrib import admin​​ 找到 ​​django/crontrib/admin/__init__.py​

def autodiscover():
autodiscover_modules(admin, register_to=site)

2、然后看 ​​admin.site​​ 指的是什么

# django/contrib/admin/sites.py
site = DefaultAdminSite()

3、然后看这个 DefaultAdminSite

class DefaultAdminSite(LazyObject):
def _setup(self):
AdminSiteClass = import_string(apps.get_app_config(admin).default_site)
self._wrapped = AdminSiteClass()

4、所以需要找 admin 的 default_site

# django/contrib/admin/apps.py

class SimpleAdminConfig(AppConfig):
"""Simple AppConfig which does not do automatic discovery."""

default_auto_field = django.db.models.AutoField
default_site = django.contrib.admin.sites.AdminSite
name = django.contrib.admin
verbose_name = _("Administration")
... ...

所有最终我们要研究的是 ​​AdminSite​​ 类的源码

这里截取部分代码展示说明,判断权限

a)类中判断权限,设定相关URL地址

Django内置权限系统源码解读_权限解读_02

b) 然后在template的HTML页面中进行判断展示权限按钮

代码位于 ​​django/contrib/admin/templates/admin/app_list.html​

Django内置权限系统源码解读_权限解读_03

好了,今天的源码解读就到这里,如果有任何问题欢迎随时交流沟通,或者关于个人公众号 DailyJobOps

django自定义认证系统原理及源码分析解读

疑问Django在​​如何自定义用户登录认证系统的时候​​,大家都会里面立马说自定义一个或者多个backend,比如通过账号+密码、邮箱+密码,邮箱+验证码、手机号+短信验证码等等。然后设置在settings中配置一个​​AUTHENTICATION_BA... 查看详情

django—内置用户权限管理(代码片段)

...、登录、验证等功能我们可以自己编写用户管理应用,但Django也有一个内置的用户权限管理系统。也是很强大的。在哪可以看到?关于用户的信息都存放在这个表中。 auth模块fromdjango.contribimportauth其中有几个常用的方法:auth... 查看详情

django(59)验证和授权(代码片段)

验证和授权概述  Django有一个内置的授权系统。他用来处理用户、分组、权限以及基于cookie的会话系统。Django的授权系统包括验证和授权两个部分。验证是验证这个用户是否是他声称的人(比如用户名和密码验证,角色验证)... 查看详情

django源码解读——从wsgi到django的httprequest请求对象生成

开始之前建议先参考一下这篇文章:https://blog.csdn.net/qq_33339479/article/details/78862156classCommand(BaseCommand):help="StartsalightweightWebserverfordevelopment."#Validationiscalledexplicitlyeachtimetheserverisrel 查看详情

django基于角色的权限控制

...l,一般用于公司的内部管理系统,如、OA,ERP,CRM。二、django的auth体系内置了一套RBAC系统。三、auth的六表关系  1、user表:用户表。  2、group表:群组表(角色表)。  3、permssion表:权限表。  4、user_groups:用户与群组... 查看详情

基于django+vue开发的社区疫情管理系统(附源码)(代码片段)

基于Django、DjangoRestframework、Vue的前后端分离的社区疫情管理系统。一、系统功能用户管理(只有管理员有权限)用户注册用户登录修改用户信息删除用户修改密码权限管理首页数据展示国内疫情数据展示国内疫情新闻近3... 查看详情

rbac——权限六张表(代码片段)

基于角色的权限控制(django内置auth体系)#RBAC:是基于角色的访问控制(Role-BasedAccessControl),公司内部系统#django的auth就是内置了一套基于RBAC的权限系统 user表permssion表group表user_groups表是user和group的中间表group_permissions表是group和p... 查看详情

django自带认证系统(代码片段)

板块用户权限密码哈希系统表单和视图工具django内置认证系统不支持的板块密码强度---->我们通过第三方包解决限制登录次数---->自定义中间件解决第三方验证,如qq登录微信登录等--->第三方包与对应的开发者文档对象级权... 查看详情

django(63)drf权限源码分析与自定义权限(代码片段)

前言上一篇我们分析了认证的源码,一个请求认证通过以后,第二步就是查看权限了,drf默认是允许所有用户访问 权限源码分析源码入口:APIView.py文件下的initial方法下的check_permissionsdefcheck_permissions(self,request):"""检查是否应... 查看详情

java线程池源码解读

java线程池的顶级类是Executors内置了几种线程池1、newFixedThreadPool 并且重载了两个此方法 有固定线程数的线程池当达到设置的线程数时 多余的任务会排队,当处理完一个马上就会去接着处理排队中的任务源码如下publics... 查看详情

django默认权限机制介绍及实践(代码片段)

演示Django版本为当前最新版本v2.2当Django配置文件中的INSTALL_APPS包含了django.contrib.auth时,就默认启用了一个简单的权限系统,提供了为用户或组分配权限的方法之所以说简单呢?主要是因为:默认的权限系统是基于表的控制,权... 查看详情

django源码分析之server(代码片段)

乍见Django内置的server基本包括两部分:django.core.servers和django.core.handlers相识servers.basehttp是Django自身提供的一个用于开发测试的server模块,其中提供的WSGIServer、ServerHandler、WSGIRequestHandler其实都是属于WSGIserver,django只不过是对pytho... 查看详情

ugui源码解读-布局系统(代码片段)

CanvasUpdate枚举类:CanvasUpdate,用于表示当前的布局阶段publicenumCanvasUpdate///<summary>///Calledbeforelayout.///</summary>Prelayout=0,///<summary>///Calledforlayout.///</summ 查看详情

ugui源码解读-布局系统(代码片段)

CanvasUpdate枚举类:CanvasUpdate,用于表示当前的布局阶段publicenumCanvasUpdate///<summary>///Calledbeforelayout.///</summary>Prelayout=0,///<summary>///Calledforlayout.///</summ 查看详情

源码解读之file(代码片段)

...文件/目录基本操作  文件/目录列表读取 文件权限访问以及文件信息设置 其他 FileSystem简介 File中有一个变量fs 类型为FileSystemcompareTo方法依赖于他而equals方法又依赖compareTohashCode也是依赖他所以说:compareT... 查看详情

django的用户模块与权限系统(代码片段)

一导言设计一个好的用户系统往往不是那么容易,Django提供的用户系统可以快速实现基本的功能,并可以在此基础上继续扩展以满足我们的需求。先看看Django的用户系统都提供哪些功能:提供用户模块(UserModel)权限验证(默认添加... 查看详情

vnpy源码阅读学习:准备工作(代码片段)

vnpy源码阅读学习目标通过阅读vnpy,学习量化交易系统的一些设计思路和理念。通过阅读vnpy学习python项目开发的一些技巧和范式通过vnpy的设计,可以用python复现一个小型简单的量化交易系统看看是否可以用java或者.net做一个类似... 查看详情

springsecurity源码解读之remembermeauthenticationfilter

...AuthenticationFilter的作用很简单,就是用于当session过期后,系统自动通过读取cookie让系统自动登录。我们来看看Springsecurity的过滤器链条。我们发现这个 RememberMeAuthenticationFilter 在匿名构造器之前,这个是为什么呢?还是从... 查看详情