心蓝的博客 心蓝的博客
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档

心蓝

人生苦短,我用python
首页
  • 零基础

    • python零基础入门
  • 专项

    • 正则表达式
  • web框架

    • django框架
    • drf
技术
  • 友情链接
关于
收藏
  • 分类
  • 标签
  • 归档
  • 零基础

  • 专项

  • web框架

    • django框架

    • drf

      • 简介
      • 序列化器
      • 请求和响应
      • 类视图
      • 视图集和路由器
      • 序列化器补充
      • 身份验证与权限
        • 身份验证
        • 权限验证
          • 如何确定权限
          • 设置权限策略
          • 内置权限
          • AllowAny
          • IsAuthenticated
          • IsAdminUser
          • IsAuthenticatedOrReadOnly
          • 自定义权限
          • 对象级权限
        • 准备工作
        • 关联项目与用户
        • 添加登录功能
        • 向视图添加权限
        • 对象级权限
      • 分页与限流
  • python
  • web框架
  • drf
心蓝
2022-12-26
目录

身份验证与权限

# 身份验证与权限

# 身份验证

身份验证是将传入请求与一组识别凭证相关联的机制。

例如请求携带的用户名密码,签名令牌等。然后权限之类的限制策略才可以使用这些凭证来确定是否应该允许请求。

身份验证始终在视图的最开始运行,在权限和限制检查发生之前,在任何其他代码被允许继续之前。

REST框架提供多种开箱即用的身份验证方案,后面项目实战时,我们再讨论。

# 权限验证

与身份验证,限流一起,权限决定是否应该授予或拒绝访问请求。

权限检查总是在视图的最开始运行,在任何其他代码被允许继续之前。权限检查通常会使用request.user和request.auth属性中的身份验证信息来确定是否应允许传入请求。

权限用于授予或拒绝不同类别的用户访问 API 的不同部分。

最简单的权限是允许任何经过身份验证的用户访问,而拒绝任何未经身份验证的用户访问。

# 如何确定权限

REST framework中权限被定义为权限列表。在运行视图的主体之前,检查列表中的每个权限。如果任何权限检查失败,将引发exceptions.PermissionDeniedorexceptions.NotAuthenticated异常,并且视图的主体将不会运行。当权限检查失败时,将根据以下规则返回“403 Forbidden”或“401 Unauthorized”响应:

  • 请求已成功验证,但权限被拒绝。— 将返回 HTTP 403 Forbidden 响应。
  • 请求未成功通过身份验证,最高优先级的身份验证类没有使用WWW-Authenticate标头。— 将返回 HTTP 403 Forbidden 响应。
  • 请求的身份验证没有成功,并且最高优先级的身份验证类使用了WWW-Authenticate头。— 将返回HTTTP 401 Unauthorized响应。

# 设置权限策略

可以使用设置全局设置默认权限策略DEFAULT_PERMISSION_CLASSES。例如。

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': [
        'rest_framework.permissions.IsAuthenticated',
    ]
}
1
2
3
4
5

如果未指定,此设置默认为允许不受限制的访问:

'DEFAULT_PERMISSION_CLASSES': [
   'rest_framework.permissions.AllowAny',
]
1
2
3

您还可以使用基于APIView类的视图,在每个视图或每个视图集的基础上设置权限策略

from rest_framework.permissions import IsAuthenticated
from rest_framework.views import APIView

class ExampleView(APIView):
    permission_classes = [IsAuthenticated]
1
2
3
4
5

或者使用基于装饰器@api_view的函数视图

from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated

@api_view(['GET'])
@permission_classes([IsAuthenticated])
def example_view(request, format=None):
    pass

1
2
3
4
5
6
7
8

注意:直接在视图上设置权限类列表后,会忽略设置文件中配置的权限类列表。

# 内置权限

# AllowAny

允许任何请求访问。

# IsAuthenticated

只允许通过身份验证的请求访问。

# IsAdminUser

只允许管理员身份的请求访问。

# IsAuthenticatedOrReadOnly

只允许通过身份验证的请求或者只读请求(get,head,option)访问。

# 自定义权限

要实现自定义权限,基础BasePermission并实现以下方法中的一个或两个:

  • .has_permission(self, request, view)
  • .has_object_permission(self, request, view, obj)

如果请求被授予访问权限,则该方法应返回True,否则返回False。

注意

对象级的has_object_permission方法只有在视图级的has_permission检查已经通过的情况下才会被调用。

如果测试失败,自定义权限应该引发一个PermissionDenied异常。若要更改与异常关联的错误消息,请在自定义权限上直接实现message属性。否则,将使用PermissionDenied的default_detail属性。类似地,要更改与异常相关的代码标识符,请直接在自定义权限上实现一个code属性——否则将使用PermissionDenied的default_code属性。

from rest_framework import permissions

class CustomerAccessPermission(permissions.BasePermission):
    message = 'Adding customers not allowed.'

    def has_permission(self, request, view):
         ...
1
2
3
4
5
6
7

# 对象级权限

REST 框架权限还支持对象级权限。对象级权限用于确定是否应允许用户对特定对象进行操作,该对象通常是模型实例。

对象级权限由REST框架的通用视图调用.get_object时运行。与视图级别权限一样,也有例外。如果不允许用户对给定对象进行操作,则会引发PermissionDenied异常。

如果您正在编写自己的视图并希望强制执行对象级权限,或者覆盖了通用视图上的get_object方法,那么您需要在视图方法中显示的调用.check_object_permissions(request, obj)

# 准备工作

为了讲清楚身份验证与权限,我们再创建一个应用projects,设计模型如下:

class Project(models.Model):
    name = models.CharField('项目名称', max_length=20, help_text='项目名称')
    desc = models.CharField('项目描述', max_length=200, help_text='项目描述', null=True, blank=True)
    leader = models.ForeignKey('auth.User', verbose_name='项目负责人', on_delete=models.SET_NULL, null=True, help_text='项目负责人')
    c_time = models.DateTimeField('创建时间', auto_now_add=True)
1
2
3
4
5

编写序列化器

from rest_framework import serializers
from .models import Project


class ProjectSerializer(serializers.ModelSerializer):
    class Meta:
        model = Project
        fields = '__all__'
1
2
3
4
5
6
7
8

编写视图集

class ProjectViewSet(ModelViewSet):
    queryset = Project.objects.all()
    serializer_class = ProjectSerializer
1
2
3

配置url

router = DefaultRouter()
router.register(r'projects', views.ProjectViewSet)
urlpatterns = [
	...
    path('', include(router.urls))
]
1
2
3
4
5
6

目前,我们的API,谁可以编辑或删除项目没有任何限制。我们希望有一些更高级的行为,以确保:

  • 项目总是与创建者相关联。
  • 只有经过身份验证的用户才能创建项目。
  • 只有项目的创建者才能更新或删除它。
  • 未经身份验证的请求应该具有完全只读访问权限。

# 关联项目与用户

现在我们可以创建项目,并手动选择对应的用户与项目进行关联。但是在某些场景下这可能不是你想要的,比如谁创建项目就把谁与创建的项目进行关联。

那么用户就不在作为一个参数了,django会将当前用户对象设置到request.user属性上,它是在中间件AuthenticationMiddleware中完成的。

我们首先修改项目的序列化器,将leader字段修改为只读,并显示用户的用户名。

leader = serializers.ReadOnlyField(source='leader.username')
1

ReadOnlyField始终是只读的,并将用于序列化表示,但当模型实例被反序列化时,将不会用于更新它们。

然后再重写项目视图上的perform_create()方法,修改实例保存的方式,处理隐式传入请求或者请求URL中的任何信息,这里我们给项目关联当前用户。

在ProjectViewSet视图集上,添加以下方法:

def perform_create(self, serializer):
	serializer.save(leader=self.request.user)  # 实例化对象时传入leader
1
2

# 添加登录功能

此时创建项目你会发现报错,这是因为,django在未做身份验证的请求的user属性上绑定了一个AnonymousUser对象。我们需要能够以用户身份登录。

通过编辑根urls.py文件中的URLconf,我们可以添加一个login视图来与可浏览的API一起使用。

在文件的末尾,添加一个模式,以包含可浏览API的登录和注销视图。

urlpatterns += [
    path('auth/', include('rest_framework.urls')),
]
1
2
3

模式的'auth/'部分实际上可以是你想使用的任何URL。

现在,如果你再次打开浏览器并刷新页面,你会在页面的右上角看到一个“Login”链接。

登录之前创建的用户,你将能够再次创建项目。

# 向视图添加权限

现在我们希望确保只有经过身份验证的用户才能创建、更新和删除项目。RESTframework包括许多权限类,我们可以使用它们来限制谁可以访问给定的视图。在这个例子中,我们要找的是IsAuthenticatedOrReadOnly,这将确保经过身份验证的请求获得读写访问权限,而未经身份验证的请求获得只读访问权限。

首先在views模块中添加以下导入

from rest_framework import permissions
1

然后,将下列属性添加到ProjectListCreateView和ProjectDetailUpdateDeleteView视图类中。

permission_classes = [permissions.IsAuthenticatedOrReadOnly]
1

现在退出登录,你会发现在项目列表页面的POST请求表单不见了,项目详情页的update,put按钮也看不见了。

# 对象级权限

再创建一个用户并登录,你会发现可以对前面用户创建的项目进行编辑。

实际上,我们希望所有项目对任何人都可见,但也要确保只有创建了项目的用户才能更新或删除它。

为此,我们需要创建一个自定义的权限。

在projects应用程序中,创建一个新文件permissions.py,编写如下代码:

from rest_framework import permissions


class IsOwnerOrReadOnly(permissions.BasePermission):
    """
    自定义权限只允许对象的leader才能编辑它
    """
    def has_object_permission(self, request, view, obj):
        if request.method in permissions.SAFE_METHODS:
            return True
        return obj.leader == request.user
1
2
3
4
5
6
7
8
9
10
11

将它添加到ProjectDetailUpdateDeleteView视图上

permission_classes = [permissions.IsAuthenticatedOrReadOnly,
                      IsOwnerOrReadOnly]
1
2

现在,再次打开浏览器,你会发现,只有当登录用户与当前代码片段的创建用户相同时,DELETE和PUT按钮才会出现在页面上。

本文完,感谢你的耐心阅读,如有需要可加我微信,备注「博客」并说明原因,我们一起进步,下次见。

#drf
上次更新: 2022/12/26, 16:59:39
序列化器补充
分页与限流

← 序列化器补充 分页与限流→

最近更新
01
requests让接口测试如此简单 原创
03-31
02
最简明的python正则教程
03-30
03
pycharm激活码
12-30
更多文章>
Theme by Vdoing | Copyright © 2019-2025 心蓝
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式