资讯专栏INFORMATION COLUMN

DRF | 针对指定的接口设置权限

gplane / 2369人阅读

摘要:描述针对同一个,对不同的接口设置不同的权限看了源码通过源码可以发现,每次请求进来,都要做认证,权限验证和限流验证。

描述

针对同一个 view,对不同的接口设置不同的权限

看了 DRF views 源码:

</>复制代码

  1. def initial(self, request, *args, **kwargs):
  2. """
  3. Runs anything that needs to occur prior to calling the method handler.
  4. """
  5. self.format_kwarg = self.get_format_suffix(**kwargs)
  6. # Perform content negotiation and store the accepted info on the request
  7. neg = self.perform_content_negotiation(request)
  8. request.accepted_renderer, request.accepted_media_type = neg
  9. # Determine the API version, if versioning is in use.
  10. version, scheme = self.determine_version(request, *args, **kwargs)
  11. request.version, request.versioning_scheme = version, scheme
  12. # Ensure that the incoming request is permitted
  13. self.perform_authentication(request)
  14. self.check_permissions(request)
  15. self.check_throttles(request)

通过源码可以发现,每次请求进来,都要做认证,权限验证和限流验证。如果所有接口都需要权限,这直接在视图类中直接设置 permission_classes 即可;如果针对业务中部分接口需要权限,其他不需要权限的场景,这样一刀切的方式是行不通的,因为进来的请求会被权限打回去,针对部分接口需要权限的场景,可能需要变通一下。

1 场景如下:例如个人博客

例如有个分类模型

</>复制代码

  1. class Category(models.Model):
  2. name = models.CharField("名称", max_length=30)
  3. created = models.DateTimeField("生成时间", auto_now_add=True)
  4. def __str__(self):
  5. return self.name

如果使用 DRF 的 viewset

</>复制代码

  1. class CategoryViewSet(viewsets.ModelViewSet):
  2. queryset = Category.objects.all()
  3. serializer_class = CategorySerializer

DRF 会自动生成六个方法,具体就不说了,对于个人博客来说,获取列表以及详情是不需要设置权限的,但是对于更新,创建,删除时需要做权限验证的,那么问题来了,我该怎么做好权限验证,这里我假使获取详情需要管理员权限,其他方法都不需要权限验证

2 变通方式 方法 1:自定义装饰器

写包装权限的装饰器

</>复制代码

  1. from functools import update_wrapper
  2. def wrap_permission(*permissions, validate_permission=True):
  3. """custom permissions for special route"""
  4. def decorator(func):
  5. def wrapper(self, request, *args, **kwargs):
  6. self.permission_classes = permissions
  7. if validate_permission:
  8. self.check_permissions(request)
  9. return func(self, request, *args, **kwargs)
  10. return update_wrapper(wrapper, func)
  11. return decorator

自定义权限类

</>复制代码

  1. from rest_framework.permissions import IsAdminUser
  2. class IsVbAdminUser(IsAdminUser):
  3. """
  4. Allows access only to admin users.
  5. """
  6. def has_object_permission(self, request, view, obj):
  7. """
  8. Return `True` if permission is granted, `False` otherwise.
  9. """
  10. return self.has_permission(request, view)

然后在 viewset 中,例如获取详情接口,这样用,

</>复制代码

  1. @wrap_permission(IsVbAdminUser)
  2. def retrieve(self, request, *args, **kwargs):

如果直接请求,就会下面错误

</>复制代码

  1. "error_message": "Authentication credentials were not provided."

后面在 DRF 的装饰器中找到 permission_classes

</>复制代码

  1. def permission_classes(permission_classes):
  2. def decorator(func):
  3. func.permission_classes = permission_classes
  4. return func
  5. return decorator

这个默认的业务还需要定制,根据自己业务需要了

方法2:使用 detail_route 或者 list_route

</>复制代码

  1. @detail_route(
  2. url_path="test",
  3. permission_classes=(IsVbAdminUser, ),
  4. )
  5. def test(self, request, *args, **kwargs):

这样耍的原因,在看了 DRF route 源代码,发现

</>复制代码

  1. method_kwargs = getattr(viewset, methodname).kwargs
  2. initkwargs = route.initkwargs.copy()
  3. initkwargs.update(method_kwargs)

是不是很好玩

对于自定义路由,使用这种方法,还是蛮方便的。

方法 3:重新定义 initial 方法

重写 initial 方法,根据方法定义不同的权限类

添加 permission_classes_map 类属性

permission_classes_map 定义接口和权限的映射,用法如下:

</>复制代码

  1. permission_classes_map = {
  2. 方法名: 权限类列表
  3. }

此为特定接口的权限检测,例如如果视图中包含 create 方法,同时在又在视图中设置了全局性的 permission_classes,
但是想为 create 定义不同于全局的权限,所以这里可以这样设置,示例如下:

</>复制代码

  1. permission_classes_map = {
  2. "create": [CustomPermission]
  3. }

</>复制代码

  1. permission_classes_map = {}
  2. def initial(self, request, *args, **kwargs):
  3. """重新定义此方法,添加灵活配置权限映射"""
  4. if request.method.lower() in self.http_method_names:
  5. handler = getattr(self, request.method.lower(), self.http_method_not_allowed)
  6. else:
  7. handler = self.http_method_not_allowed
  8. if hasattr(handler, "__name__"):
  9. handler_name = handler.__name__
  10. elif hasattr(handler, "__func__"):
  11. handler_name = handler.__func__.__name__
  12. else:
  13. handler_name = None
  14. if handler_name and handler_name in self.permission_classes_map:
  15. if isinstance(self.permission_classes_map.get(handler_name), (tuple, list)):
  16. self.permission_classes = self.permission_classes_map.get(handler_name)
  17. return super(CommonWithSignViewSet, self).initial(request, *args, **kwargs)
方法 4:使用不同的视图

把需要权限验证的和不需要权限验证的视图分开,写两个视图,但是这样做会冗余部分代码

3 小结

我在以往的经历中,如果是针对默认的路由需要加权限验证,我会使用方法 1,对于自定义路由,我会使用方法 2,当然也可以反过来思考,设置全局的权限验证,如果那个方法不需要权限验证,使用装饰器把权限设置为空即可,随便怎么折腾

前两种方法尽量不要再视图属性中设置 permission_classes,这样的处理有点奇葩,个人可以针对 DRF 处理流程,进行包装,看业务需要。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/45444.html

相关文章

  • DRF | 针对指定接口设置权限

    摘要:描述针对同一个,对不同的接口设置不同的权限看了源码通过源码可以发现,每次请求进来,都要做认证,权限验证和限流验证。 描述 针对同一个 view,对不同的接口设置不同的权限 看了 DRF views 源码: def initial(self, request, *args, **kwargs): Runs anything that needs to occur pr...

    TANKING 评论0 收藏0
  • DRF | 针对指定接口设置权限

    摘要:描述针对同一个,对不同的接口设置不同的权限看了源码通过源码可以发现,每次请求进来,都要做认证,权限验证和限流验证。 描述 针对同一个 view,对不同的接口设置不同的权限 看了 DRF views 源码: def initial(self, request, *args, **kwargs): Runs anything that needs to occur pr...

    用户83 评论0 收藏0
  • Django-缓存

    摘要:自带了一个健壮的缓存系统来保存动态页面,避免每次请求都重新计算。缓存中的和方法是很常见的。尽量放在第一个继承的类设置过期时间根据自己需求加缓存。目前这个缓存使用的是内存。 概述:对于中等流量的网站来说,尽可能的减少开销是非常必要的。缓存数据就是为了保存那些需要很多计算资源的结果,这样的话就不必在下次重复消耗计算资源。获取数据的数据的时候就是去缓存中拿,拿到了直接返回,没拿到就去数据库中...

    aervon 评论0 收藏0
  • 记录django-rest-framework处理微信支付notify_url遇到问题

    摘要:微信支付统一下单接口,有一个叫的参数,作用我就照搬官方文档异步接收微信支付结果通知的回调地址,通知必须为外网可访问的,不能携带参数。 最近在做一个小程序,小程序有涉及到微信支付,说来惭愧,还是第一次自己动手去做微信支付这一块的实现,所以过程中遇到了很多人都会踩的坑(例如mmp的微信支付各种key各种id要在哪里找,很难找),这次使用django来开发,接口部分用django-rest-...

    NicolasHe 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<