[ django权限认证 ]


Django权限认证

Django 自带了一个内置的权限系统。它提供了一种将权限分配给特定用户和用户组的方式。

它被 Django 管理站点使用,但欢迎你在自己的代码中使用它。

Django 管理站点使用权限如下:

查看对象的访问权限仅限于具有该类型对象的 "view" 或 "change" 权限的用户。 查看 "添加" 表单并添加对象的访问权限仅限于具有该类型对象的 "add" 权限的用户。 访问更改列表、查看 "更改" 表单和更改对象的权限仅限于具有该类型对象的 "change" 权限的用户。 删除对象的权限仅限于具有该类型对象的 "delete" 权限的用户。 权限不仅可以按对象类型设置,还可以按特定对象实例设置。通过使用 ModelAdmin 类提供的 has_view_permission()、has_add_permission()、has_change_permission() 和 has_delete_permission() 方法,可以为相同类型的不同对象实例自定义权限。

User 对象有两个多对多字段:groups 和 user_permissions。User 对象可以以与任何其他 Django 模型 相同的方式访问其相关对象:

myuser.groups.set([group_list])
myuser.groups.add(group, group, ...)
myuser.groups.remove(group, group, ...)
myuser.groups.clear()
myuser.user_permissions.set([permission_list])
myuser.user_permissions.add(permission, permission, ...)
myuser.user_permissions.remove(permission, permission, ...)
myuser.user_permissions.clear()

默认权限

当在你的 INSTALLED_APPS 配置中列出了 django.contrib.auth 时,它会确保为每个在你已安装的应用程序中定义的 Django 模型创建四个默认权限:添加、更改、删除和查看。

这些权限将在运行 manage.py migrate 时创建;在将 django.contrib.auth 添加到 INSTALLED_APPS 后首次运行 migrate 时,将为所有先前安装的模型创建默认权限,以及在此时安装的任何新模型。此后,每次运行 manage.py migrate 时(创建权限的函数与 post_migrate 信号相关联),都会为新模型创建默认权限。

假设你有一个带有 app_label 为 foo 的应用程序,并且有一个名为 Bar 的模型,要测试基本权限,你应该使用以下方式:

添加:user.has_perm('foo.add_bar')
修改:user.has_perm('foo.change_bar')
删除:user.has_perm('foo.delete_bar')
查看:user.has_perm('foo.view_bar')
Permission 模型很少直接访问。

drf-permission

django-guardian

request.user.has_perms(['task.add_task', 'task.assign_task']) 这里权限需要全部通过才返回true

实现对象级别权限(需要安装django-guardian)
user2 = User.objects.get(username="test2")
task2 = Task.objects.get(summary="task2")
# assign_perm("assign_task", user2, task2)
print(user2.has_perm("assign_task", task2)) # has_perm第二个参数, 必须要填,

表结构分析

  1. Group表(主要字段: name, permissions)
class Group(models.Model):
    name = models.CharField(_("name"), max_length=150, unique=True)
    permissions = models.ManyToManyField(
        Permission,
        verbose_name=_("permissions"),
        blank=True,
    )

    objects = GroupManager()

    class Meta:
        verbose_name = _("group")
        verbose_name_plural = _("groups")

    def __str__(self):
        return self.name

    def natural_key(self):
        return (self.name,)
  1. permission表(主要字段 name, content_type, codename)
class Permission(models.Model):
    name = models.CharField(_("name"), max_length=255)
    content_type = models.ForeignKey(
        ContentType,
        models.CASCADE,
        verbose_name=_("content type"),
    )
    codename = models.CharField(_("codename"), max_length=100)

    objects = PermissionManager()

    class Meta:
        verbose_name = _("permission")
        verbose_name_plural = _("permissions")
        unique_together = [["content_type", "codename"]]
        ordering = ["content_type__app_label", "content_type__model", "codename"]

    def __str__(self):
        return "%s | %s" % (self.content_type, self.name)

    def natural_key(self):
        return (self.codename,) + self.content_type.natural_key()

    natural_key.dependencies = ["contenttypes.contenttype"]
  1. 模型表
class ContentType(models.Model):
    app_label = models.CharField(max_length=100)
    model = models.CharField(_("python model class name"), max_length=100)
    objects = ContentTypeManager()

    class Meta:
        verbose_name = _("content type")
        verbose_name_plural = _("content types")
        db_table = "django_content_type"
        unique_together = [["app_label", "model"]]

案例演示:

class UserAddGroup(View):
    def get(self, request):
        user = User.objects.get(username='chf')
        group = Group.objects.create(name='group1')
        group.permissions.add(Permission.objects.get(codename='add_chf'))
        user.groups.add(group)
        return JsonResponse({"msg": "ok"})

DRF权限

  1. DjangoModelPermissions(主要是针对当前model判断是否有此app的权限) Get都有权限查询 POST, PUT, PATCH, DELETE才需要进行校验
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'],
    }
from rest_framework.permissions import DjangoModelPermissions

class ChfAuthentication(BaseAuthentication):
    def authenticate(self, request):
        user = User.objects.get(username='chf')
        user.user_permissions.remove(Permission.objects.get(codename='change_chf'))
        user.user_permissions.remove(Permission.objects.get(codename='delete_chf'))
        return (user, None)


class ChfSerializer(serializers.HyperlinkedModelSerializer):
    class Meta:
        model = Chf
        fields = '__all__'

class ChfAppModelViewSet(viewsets.ModelViewSet):
    queryset = Chf.objects.all()
    serializer_class = ChfSerializer
    authentication_classes = [ChfAuthentication]
    permission_classes = [DjangoModelPermissions]

对象级别权限(序列化器中的self.get_object()内部触发的has_object_permission()方法)

from rest_framework.permissions import DjangoObjectPermissions

...
...
...
class ChfAppModelViewSet(viewsets.ModelViewSet):
    queryset = Chf.objects.all()
    serializer_class = ChfSerializer
    authentication_classes = [ChfAuthentication]
    permission_classes = [DjangoModelPermissions]


    def retrieve(self, request, *args, **kwargs):
        self.get_object()
        return super().retrieve(request, *args, **kwargs)
    def destroy(self, request, *args, **kwargs):
        self.perform_destroy()
        return super().destroy(request, *args, **kwargs)
    def list(self, request, *args, **kwargs):
        return super().list(request, *args, **kwargs)
    def update(self, request, *args, **kwargs):
        self.perform_update()
        return super().update(request, *args, **kwargs)
    def create(self, request, *args, **kwargs):
        self.perform_create()
        return super().create(request, *args, **kwargs)


# 除了在self.get_object()中默认触发has_object_permission, 也可以重写perform_...方法触发has_object_permission对象级别权限验证
    def perform_update(self, serializer):
        # 进行权限检查
        if not self.has_object_permission(self.request, self.get_object()):
            return Response({"error": "Permission denied."}, status=status.HTTP_403_FORBIDDEN)

        # 如果权限检查通过,则保存数据到数据库
        serializer.save()