Fork me on GitHub

Python系列之《Django-DRF-权限》

DRF系列的姊妹篇,了解一下.

一、认证

1. 启用DRF登录接口

在drf源码文件rest_framework.urls.py中能看到如下信息:

1
2
3
4
urlpatterns = [
url(r'^login/$', login, login_kwargs, name='login'),
url(r'^logout/$', logout, name='logout'),
]

会发现DRF在这里将登录、登出的接口都已经写好了,直接使用即可。

全局urls.py文件里增加如下配置,来启用登录接口:

1
2
3
4
urlpatterns = [
······
url(r'^api-auth', include("rest_framework.urls", namespace="rest_framework")),
]

然后,去页面登录:

输入账号登录,查看浏览器保存的Session:

你在数据库中,也能看到Session的存储信息.

2. User接口使用Session登录认证

这里来个使用案例,了解下如何在App里使用认证;
vim users/views.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from rest_framework.authentication import SessionAuthentication
from rest_framework.permissions import IsAuthenticated


class UserViewset(viewsets.ReadOnlyModelViewSet):
"""
retrieve:
返回指定用户信息
list:
返回用户列表
"""
queryset = User.objects.all()
serializer_class = UserSerializer
authentication_classes = (SessionAuthentication, ) # 将认证的key, 存放在数据库里;[局部使用方法/可以全局配置]
permission_classes = (IsAuthenticated, ) # 启用登录认证,即:登录就可以访问.

把上面的登录用户退出,然后访问User接口:

用户没有登录,不让访问信息,认证成功。只有在用户登录时,才可访问用户数据。

3. Session认证全局配置

vim setting.py

1
2
3
4
5
6
7
8
REST_FRAMEWORK = {
······
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
),
}

当然,除了SessionAuthentication认证以外,还有其他认证方式,也可以配置多个,取决于你使用到什么就配置什么好了。

那现在就可以去将User里的Session配置去掉,访问效果一样。

本文只讲了SessionAuthentication的认证方式和使用,其他认证方式在后面文章中会陆续讲解。

那认证讲完了,接下来看看权限.

二、权限

1. 简单了解权限

其实在上面已经使用过权限了,你注意到了吗?

上面users/views.py文件里写到:

1
permission_classes = (IsAuthenticated, ) # 启用登录认证,即:登录就可以访问.

这个配置是指定登录认证,其实还有很多种认证方式,如下:

  • IsAuthenticated - 登录权限
  • IsAdminUser - 管理员权限
  • IsAuthenticatedOrReadOnly - 登录或只读权限
  • DjangoModelPermissions - 模型权限验证权限
  • DjangoModelPermissionsOrAnonReadOnly - 模型权限只读认证
  • DjangoObjectPermissions - 对象权限认证

当然,这些认证都基于BasePermission权限,源码内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class BasePermission(object):
"""
A base class from which all permission classes should inherit.
"""

def has_permission(self, request, view):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True

def has_object_permission(self, request, view, obj):
"""
Return `True` if permission is granted, `False` otherwise.
"""
return True

  • has_permission,检查权限
  • has_object_permission,检查对象权限

我们也可以继承BasePermission权限,进行自定义权限。

2. DjangoModelPermissions

Django模型权限,是我们用的最多的,这里也准备对它的使用展开讲解下。

1
2
3
4
5
6
7
8
9
10
11
class DjangoModelPermissions(BasePermission):

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'],
}

从它的源码中能看出,这个权限给我们设置好了很多方法的权限认证,增删改查的接口操作都给做了,我们只需要使用它即可。不得不感叹DRF的强大,以前在使用MVC框架模式开发中,权限得一个个去控制,其过程可以用恶心来形容。

DRF的安全请求方法:
在rest_framework.permissions中配置了如下安全方法,

1
SAFE_METHODS = ('GET', 'HEAD', 'OPTIONS')

也就是只允许这些方法在没有权限的情况下,能访问资源。

2.1 配置DjangoModelPermissions权限

vim setting.py

1
2
3
4
5
6
REST_FRAMEWORK = {
······
'DEFAULT_PERMISSION_CLASSES': (
'rest_framework.permissions.DjangoModelPermissions',
),
}

直接在全局配置文件里进行配置,

优点: 不用在每个视图里去写了,
缺点: 如果你一但写一个viewset没有queryset的话,将会出错,解决:使用permission_classes = (IsAuthenticated, ) 写在你的viewset里,自己定义其他认证方式,覆盖全局的配置即可。

2.2 权限验证

使用普通用户登录,查看idc的一个资源:

可以看到只允许安全方法中 GET方法 来访问数据,而曾,删,改方法,都被禁止。

给nicksors-1授权idc的删除权限:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
In [1]: from django.contrib.auth.models import User

In [2]: from django.contrib.auth.models import Permission

In [3]: user = User.objects.get(username='nicksors-1')

In [4]: Permission.objects.get(pk=21)
Out[4]: <Permission: idcs | idc | Can delete idc>

In [5]: del_idc = Permission.objects.get(pk=21)

In [6]: user.user_permissions = [del_idc]
manage.py:1: RemovedInDjango20Warning: Direct assignment to the forward side of a many-to-many set is deprecated due to the implicit save() that happens. Use user_permissions.set() instead.
#!/usr/bin/env python

In [7]: user.user_permissions.all()
Out[7]: <QuerySet [<Permission: idcs | idc | Can delete idc>]>

再次刷新页面,你能看到DELETE权限已经有了,其他权限就不挨个测试了哈,原理一样。

2.3 自定义GET权限

因为GET方法在DjangoModelPermissions权限认证中,属于安全权限, 意思是可以直接查看。

试想一下,如果我们有接口不想被GET查看,那如何控制呢?这就需要我们自己来定义实现了。

动刀:
在 setting.py同级目录下创建permissions.py文件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
from rest_framework.permissions import DjangoModelPermissions


class ModelPermissions(DjangoModelPermissions):

def get_custom_perms(self, view, method): # 接收请求的视图,以及请求方法
if hasattr(view, 'extra_perm_map'): # 判断视图是否定义了extra_perm_map
if isinstance(view.extra_perm_map, dict): # 判断extra_perm_map属性必须是一个dict
return view.extra_perm_map.get(method, []) # 返回extra_perm_map里定义的参数,默认为空list
return []

def has_permission(self, request, view):
# Workaround to ensure DjangoModelPermissions are not applied
# to the root view when using DefaultRouter.
if getattr(view, '_ignore_model_permissions', False):
return True

if not request.user or (
not request.user.is_authenticated and self.authenticated_users_only):
return False

queryset = self._queryset(view)
perms = self.get_required_permissions(request.method, queryset.model)
perms.extend(self.get_custom_perms(view, request.method)) # 两个列表相加,将请求方法加入DjangoModelPermissions里的perms_map中.
return request.user.has_perms(perms)

  1. 自定义了ModelPermissions权限,继承DjangoModelPermissions权限类;
  2. 上面这一串代码其实就是在更改DjangoModelPermissions里的perms_map列表,将perms_map列表里原有GET = [] 改写成我们自定义的权限认证信息. 且看下面如何使用。

2.4 配置全局使用

在全局配置文件中将DjangoModelPermissions注释,换成我们上面自定义的ModelPermissions权限类

1
2
3
4
5
6
7
REST_FRAMEWORK = {
······
'DEFAULT_PERMISSION_CLASSES': (
# 'rest_framework.permissions.DjangoModelPermissions',
'ops.permissions.ModelPermissions',
),
}

2.5 使用自定义GET权限

还是拿User作为示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
class UserViewset(viewsets.ReadOnlyModelViewSet):
"""
retrieve:
返回指定用户信息
list:
返回用户列表
"""
queryset = User.objects.all()
serializer_class = UserSerializer
······
extra_perm_map = {
"GET": ['auth.view_user', ]
}

这里extra_perm_map字段指定了GET方法,意思是User接口的GET方法得有auth.view_user权限才能访问。

访问User接口页面:

嗯,显示没有GET权限,满足我们的需求,哈哈。

当然,auth.view_user权限默认是没有的,你需要添加权限,然后赋权给登录的用户,才能使用这个权限。

3 视图接口控制权限

DjangoModelPermissions的perms_map变量里记录着增删改查的权限,那viewset视图如何控制这些权限呢?

先介绍下rest_framework.viewsets里的模型类:

视同中常用的模型是ModelViewSet,因为它帮我们实现了增删改查操作,那有些接口只能有增加权限,这里如何控制?

答案很简单:通过在视图里指定模型方法来控制权限,例如:

1
2
3
class UserViewset(viewsets.GenericViewSet,
mixins.ListModelMixin,
mixins.UpdateModelMixin,):

相信你很容易能看出,这个接口只允许GET和PUT请求,我们来访问看看

获取用户列表是没问题的,这是mixins.ListModelMixin定义的功能;

获取和更新单个用户:

提示GET方法不被允许,而PUT可以,什么原因呢?如果有好好看文章,相信你知道是什么原因!这里就不细说了。

视图的接口权限,就是通过以上方式来控制的。

写到这里,作者将自己常用的权限使用方法分享给大家啦,当然肯定有些信息介绍的没那么详细,也适当锻炼下大家看看官网或者其他文章,哈哈。

文完。

-------------本文结束感谢您的阅读-------------