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第二个参数, 必须要填,
表结构分析
- 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,)
- 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"]
- 模型表
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权限
- 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()