django权限管理

Django权限管理

     权限是能够约束用户行为和控制页面显示内容的一种机制。一个完整的权限应该包含用户,对象和权限3个要素,即什么用户对什么对象有什么样的权限。

Django自带的权限机制仅针对模型,即一个用户如果对Article模型有change权限,那么该用户对所有文章都有修改权限。如果实现对单个对象的权限管理,可以借助于第三方库guardian。本文将从分别从自带的“auth模块”权限机制和guardian展开介绍。

一、 自带权限机制

auth模块是Django提供的标准权限管理系统,可以提供用户身份认证, 用户组和权限管理。

1.1表结构

与 auth 模块有关的数据库表有 6 张,分别是

项目

作用

备注

auth_user.

用户信息

 

auth_group

组信息

每个组拥有id和name两个字段

auth_user_groups

user和group之间的关系

 

auth_user_user_permissions

user和prmission之间的关系

 

auth_permission

权限

每条权限拥有id ,name , 

content_type_id, codename四个字段

auth_group_permissions

用户组和权限的对应关系

使用用户组管理权限是一个更方便的方法,Group中包含多对多字段permissions

1.2相关操作

1.2.1 用户操作

创建用户

from django.contrib.auth.models import User

user = User.objects.create_user(username, email, password)
user.save()
 

用户认证

from django.contrib.auth import authenticate
user = authenticate(username=username, password=password)

认证用户的密码是否有效, 若有效则返回代表该用户的user对象, 若无效则返回None。

 

修改用户密码

user = auth.authenticate(username=username, password=old_password)
if user is not None:
    user.set_password(new_password)
user.save()
 

登录

user = authenticate(username=username, password=password)
if user is not None:
    if user.is_active:
        login(request, user)
 

退出登录

from django.contrib.auth import logout
def logout_view(request):
    logout(request)

1.2.2 组操作

django.contrib.auth.models.Group定义了用户组的模型, 每个用户组拥有id和name两个字段,该模型在数据库被映射为auth_group数据表。User对象中有一个名为groups的多对多字段,多对多关系由auth_user_groups数据表维护。Group对象可以通过user_set反向查询用户组中的用户。

 

创建组

group = Group.objects.create(name=group_name)

group.save()

 

删除组

group.delete()

 

用户加入用户组

user.groups.add(group)

#或者

group.user_set.add(user)

 

用户退出用户组

user.groups.remove(group)

#或者

group.user_set.remove(user)

 

用户退出所有用户组

user.groups.clear()

 

用户组中所有用户退出组

group.user_set.clear()

1.2.3 权限操作

检查用户权限

user.has_perm方法用于检查用户是否拥有操作某个模型的权限,若拥有权限则返回True。

user.has_perm('blog.add_article')

user.has_perm('blog.change_article')

user.has_perm('blog.delete_article')

 

抛出异常的权限检查

has_perm仅是进行权限检查, 即是用户没有权限它也不会阻止程序员执行相关操作。

@permission_required装饰器可以代替has_perm并在用户没有相应权限时重定向到登录页或者抛出异常。

# permission_required(perm[, login_url=None, raise_exception=False])

 

@permission_required('blog.add_article')

def post_article(request):

pass

 

 

每个模型默认拥有增(add), 改(change), 删(delete)权限。

添加用户权限

user.user_permissions.add(permission)

 

删除用户权限:

user.user_permissions.delete(permission)

 

清空用户权限:

user.user_permissions.clear()

用户拥有他所在用户组的权限, 使用用户组管理权限是一个更方便的方法。Group中包含多对多字段permissions, 在数据库中由auth_group_permissions数据表维护。

添加组权限:

group.permissions.add(permission)

 

删除组权限:

group.permissions.delete(permission)

 

清空组权限:

group.permissions.clear()

 

某个用户所在用户组的权限

user_A.get_group_permissions()

 

某个用户的所有权限

user_A.get_all_permissions()

 

自定义权限

在定义Model时可以使用Meta自定义权限:

class Discussion(models.Model):

  ...

  class Meta:

      permissions = (

          ("create_discussion", "Can create a discussion"),

          ("reply_discussion", "Can reply discussion"),

      )

 

判断用户是否拥有自定义权限:

user.has_perm('blog.create_discussion')

二、 guardian对象级别的权限控制

2.1 表结构

guardian_groupobjectpermission

组与model以及model内的具体object的权限对应关系

 

guardian_usrerobjectpermission

用户与model以及model内的具体object的权限对应关系

 

以guardian_groupobjectpermission表为例,说下各字段的含义

id:默认主键

  • object_pk:object的id,标识具体是哪个对象需要授权,对应的是具体的某一条数据

content_type_id:记录具体哪个表的id,对应的是django系统表django_content_type内的某条数据,django所有注册的model都会在这个表里记录

group_id:记录是那个组的用户会有权限,对应的是auth_group表里的某条记录

permission_id:记录具体的某个权限,对应的是auth_permission表里的某条记录

 

2.2 相关操作

2.2.1 权限分配

guardian.shortcuts.assign(perm, user_or_group, obj=None)

这个方法接受3个参数:perm,这个参数是一个字符串,代表一个许可,格式必须为<app>.<perm_codename>或者<perm_codename>。但是如果第三个参数是None,则必须为<app>.<perm_codename>格式。因此建议还是统一使用<app>.<perm_codename>格式。注意app并不是app的全路径,而是最后一级的模块名。user_or_group,参数是一个User或Group类型的对象。obj,这个参数就是相关的对象了。改参数是可省略的,如果省略则赋予Model权限。

 

赋予模型级别的权限
from guardian.shortcuts import assign
user = User.objects.create(username='liuyong')
assign('app.view_task', user)
user.has_perm('app.view_task') >>True

 

赋予对象级别的权限
user = User.objects.get(username='liuyong')
user.user_permissions.clear()
task = Task.objects.create(summary='Some job', content='')
assign('app.view_task', user, task)

设置group权限
>>> group = Group.objects.create(name='employees')
>>> assign('change_task', group, task)

删除用户权限
guardian.shortcuts.remove_perm(perm,user_or_group=None, obj=None)

样例代码:
>>> from guardian.shortcuts import remove_perm
>>> remove_perm('change_site', user, site)
>>> user = User.objects.get(username='joe') #刷新user对象缓存
>>> user.has_perm('change_site', site)
False

 

2.2.2 其他函数

除了Django的user.has_perm方法之外,guardian提供了一些帮助函数。

 

根据用户和对象,获取权限:

guardian.shortcuts.get_perms(user_or_group,obj)

例如:'permcodename' in get_perms(group,obj)来判断该组是否有这个权限,因为group没有has_perm方法。

 

根据用户和权限获取对象

guardian.shortcuts.get_objects_for_user(user, perms, klass=None, use_groups=True, any_perm=False)

例如:get_objects_for_user(user,'app.change_post')

>>所有可编辑的帖子

第二个参数也可以写成列表(同时满足):

>>> get_objects_for_user(user, ['engine.commontask_run', 'engine.commontask_change'])

<QuerySet [<CommonTask: Dev-Coffee-Web-发布>]>

想要仅满足列表中的任意一个权限,可以添加第三个参数any_perm=True

>>> get_objects_for_user(user, ['engine.commontask_run', 'engine.commontask_change'], any_perm=True)

<QuerySet [<CommonTask: Dev-Coffee-Web-发布>, <CommonTask: Qa-Coffee-Web-发布>, <CommonTask: dev-ops-coffee-build>]>

 

获取对象的所有权限:

>>> from guardian.shortcuts import get_users_with_perms

>>> get_users_with_perms(task)

<QuerySet [<User: dev@ops-coffee.cn>, <User: qa@ops-coffee.cn>, <User: test@ops-coffee.cn>]>

可以让超级用户也在其中:

get_users_with_perms(task, with_superusers=True)

返回的结构是以用户为key权限为value的一个字典,看起来清晰明

>>> get_users_with_perms(task, with_superusers=True, attach_perms=True)

如果我们仅想查看具有某个权限的用户,可以设置only_with_perms_in参数

>>>get_users_with_perms(task, with_superusers=True, only_with_perms_in=['commontask_change'])

<QuerySet [<User: admin@163.com>, <User: qa@ops-coffee.cn>]>

只想查看直接赋予用户的权限,而并非间接通过group取得的权限用户列表,我们可以设置参数with_group_users=False,此参数默认为True

>>> get_users_with_perms(task, with_superusers=True, with_group_users=False)

<QuerySet [<User: admin@163.com>, <User: dev@ops-coffee.cn>, <User: qa@ops-coffee.cn>]>

 

准备模型和自定义权限

from django.db import models

from django.contrib.auth.models import User

class Task(models.Model):

    summary = models.CharField(max_length=32)

    content = models.TextField()

    reported_by = models.ForeignKey(User)

    created_at = models.DateTimeField(auto_now_add=True)

 

    class Meta:

        permissions = (

                ('view_task', 'View task'),

            )

说明:permissions使我们自定义的权限。默认情况下Django为每个模型注册4个权限

view_模型名
add_模型名
change_模型名
delete_模型名

 

判断权限的包装器

guadian.core.ObjectPermissionChecker

主要有has_perm(perm,obj)和get_perms(obj)两个方法。并且提供缓存机制,在多次查找权限的时候,可以使用它。

>>> epser = User.objects.get(username='esper')
>>> site = Site.objects.get_current()
>>> from guardian.core import ObjectPermissionChecker
>>> checker = ObjectPermissionChecker(esper) # 我们也可以传入组group对象
>>> checker.has_perm('change_site', site)
True
>>> checker.has_perm('add_site', site) # 这次将不会产生数据库查询
False
>>> checker.get_perms(site)
[u'change_site']

 

2.2.3 孤儿对象许可

所谓孤儿许可,大多数情况下,可能没事,但一旦发生,后果有可能非常严重。比方说,有一个对象A。我们通过权限设置,设定joe用户对该对象有着编辑权限。忽然有一天,用户joe被删除了。可想而知,我们分配而产生的UserObjectPermission对象仍然在数据库里面,记录着:joe 有对A的编辑权限。又有一天,一个用户注册了一个用户,用户username为joe。因为之前的那个纪录,joe用户拥有对A的编辑权限。而此joe非彼joe,我们犯了一个大错误!

因此,当删除User和相关Object时,一定要删除其相关的所有UserObjectPermission和GroupObjectPermission对象。要解决这个问题有三种,一是显式编码,二是通过其提供的自定义django命令:$ python manage.py clean_orphan_obj_perms,三是定期调用guardian.utils.clean_orphan_obj_perms(),该函数会返回删除的对象数目。也可使用celery定期调度任务。

 

参考文献:

https://blog.csdn.net/weixin_42134789/article/details/84567337

官网:https://django-guardian.readthedocs.io/en/stable/userguide/remove.html

posted on 2021-08-16 22:47  星星眨着眼  阅读(3678)  评论(0编辑  收藏  举报

导航