丛FBC到CBV三(权限)
权限
准备数据表
用户组(group) | |
id | group_name |
1 | usual |
2 | vip |
3 | svip |
4 | admin |
用户(user) | |||
id | username | password | group_id |
1 | Joshua | 123 | 1 |
2 | William | 123 | 2 |
3 | Daniel | 123 | 3 |
4 | Michael | 123 | 4 |
创建项目及app:
models.py
19
1
# -*- coding:utf-8 -*-
2
from django.db import models
3
4
class Group(models.Model):
5
id = models.AutoField(primary_key=True)
6
group_name = models.CharField(max_length=40)
7
8
class Meta:
9
db_table = 'group'
10
11
class User(models.Model):
12
id = models.AutoField(primary_key=True)
13
username = models.CharField(max_length=40,unique=True)
14
password = models.CharField(max_length=40)
15
group_id = models.ForeignKey(Group, default=1)
16
17
class Meta:
18
db_table = 'user'
19
views.py
16
1
from django.http.response import JsonResponse
2
from rest_framework.views import APIView
3
4
from permissions.models import User, Group
5
6
7
class Users(APIView):
8
def get(self, request):
9
users = User.objects.all().values()
10
return JsonResponse(list(users), safe=False)
11
12
13
class Groups(APIView):
14
def get(self, request):
15
groups = Group.objects.all().values()
16
return JsonResponse(list(groups), safe=False)
urls.py
9
1
from django.conf.urls import url
2
from django.contrib import admin
3
from permissions.views import Users, Groups
4
5
urlpatterns = [
6
url(r'^admin/', admin.site.urls),
7
url(r'^user/$', Users.as_view(), name='user'),
8
url(r'^group/$', Groups.as_view(), name='group'),
9
]
Postman提交请求:
现在新建了一张MemberPrograms表,里面的内容是只给会员用户展示的
实现这个功能:
会员项目(member_programs) | |
id | program_name |
1 | 书法长卷 |
2 | 书法碑帖 |
3 | 墓志塔铭 |
4 | 兰亭集序 |
定义models
6
1
class MemberProgram(models.Model):
2
id = models.AutoField(primary_key=True)
3
program_name = models.CharField(max_length=100)
4
5
class Meta:
6
db_table = 'member_program'
定义url以及视图函数:
9
1
from django.conf.urls import url
2
3
from permissions.views import Users, Groups, MemberPrograms
4
5
urlpatterns = [
6
url(r'^user/$', Users.as_view(), name='user'),
7
url(r'^group/$', Groups.as_view(), name='group'),
8
url(r'^program/$', MemberPrograms.as_view(), name='program'),
9
]
4
1
class MemberPrograms(APIView):
2
def get(self, request):
3
programs = MemberProgram.objects.all().values()
4
return JsonResponse(list(programs), safe=False)
测试:
现在接口已经实现了,但是我们要对这个接口增加权限控制,只允许vip,svip,admin用户访问,代码实现:
方法一:
上一章我们实现了自定义认证的中间件,现在可以利用起来,修改如下:
25
1
class MyAuthentication(BaseAuthentication):
2
def authenticate(self, request):
3
name = request._request.GET.get('username')
4
print(name)
5
return (name, None)
6
7
8
class MemberPrograms(APIView):
9
authentication_classes = [MyAuthentication, ]
10
11
def get(self, request):
12
if not request.user: # 没有用户身份,不允许访问
13
ret = {'code': 1002, 'error': '权限被拒'}
14
return JsonResponse(ret)
15
username = request.user
16
try:
17
group_name = User.objects.get(username=username).group.group_name
18
except User.DoesNotExist: # 用户身份不存在,返回错误信息
19
ret = {'code': 1003, 'error': '用户不存在'}
20
return JsonResponse(ret)
21
if group_name == 'usual': # 是普通用户,没有权限
22
ret = {'code': 1002, 'error': '权限被拒'}
23
return JsonResponse(ret)
24
programs = MemberProgram.objects.all().values() # 用户权限满足条件 返回接口信息
25
return JsonResponse(list(programs), safe=False)
测试:
上面实现了接口对用户权限的控制,实际项目代码不会这么简单,需要通过token进行判断,这里只是简单实现
方法二:
利用restframework的permission组件实现:
x
1
from rest_framework.authentication import BaseAuthentication
2
from rest_framework.permissions import BasePermission
3
from rest_framework.exceptions import PermissionDenied
4
5
lass MyAuthentication(BaseAuthentication):
6
def authenticate(self, request):
7
name = request._request.GET.get('username')
8
print(name)
9
return (name, None)
10
11
12
class MyPermission(BasePermission):
13
def has_permission(self, request, view):
14
if not request.user:
15
raise PermissionDenied('权限被拒')
16
username = request.user
17
try:
18
group_name = User.objects.get(username=username).group.group_name
19
except User.DoesNotExist:
20
raise PermissionDenied('用户不存在')
21
if group_name == 'usual':
22
raise PermissionDenied('权限被拒')
23
return True
24
25
26
class MemberPrograms(APIView):
27
authentication_classes = [MyAuthentication, ]
28
permission_classes = [MyPermission, ]
29
30
def get(self, request):
31
programs = MemberProgram.objects.all().values()
32
return JsonResponse(list(programs), safe=False)
上面的例子中我们都是将认证类和权限类注册在了对应的view视图中,
其实要是项目中多数视图需要进行以上验证,那就可将自定义的认证类和权限类放在一个单独的文件中,然后注册到seeting.py中 :
在seeting.py中添加下面内容:
源码分析:
1.请求的封装,将原先的request封装到restframework的APIview中,丰富了request内容,包含了authenticators等
2. initial() 初始化
x
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
7
# Perform content negotiation and store the accepted info on the request
8
neg = self.perform_content_negotiation(request)
9
request.accepted_renderer, request.accepted_media_type = neg
10
11
# Determine the API version, if versioning is in use.
12
version, scheme = self.determine_version(request, *args, **kwargs)
13
request.version, request.versioning_scheme = version, scheme
14
15
# Ensure that the incoming request is permitted
16
17
# 身份验证
18
self.perform_authentication(request)
19
# 权限验证
20
self.check_permissions(request)
21
self.check_throttles(request)
调用了self.check_permissions(request)
x
1
def check_permissions(self, request):
2
"""
3
Check if the request should be permitted.
4
Raises an appropriate exception if the request is not permitted.
5
"""
6
for permission in self.get_permissions(): # 1
7
if not permission.has_permission(request, self): # 2
8
self.permission_denied( # 3
9
request, message=getattr(permission, 'message', None)
10
)
第一步: self.get_permissions:
x
1
def get_permissions(self):
2
"""
3
Instantiates and returns the list of permissions that this view requires.
4
"""
5
return [permission() for permission in self.permission_classes]
6
7
8
self.permission_classes 就是我们定义的permissions,默认是从seeting.py配置文件中找,如果我们的view中制定了这一项,就使用view中的permission,而不用配置文件中的
9
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
第二步:permission.has_permission
此时的permission就是定义的permission类,既然调用了has_permission方法,那我们的permission类就必须实现这个方法,这个方法中用来实现我们的权限校验逻辑
源码判断这个函数,if not,说明如果has_permission函数的返回值不是True,就到第三步,否则集序循环下一个permission
第三步:self.permission_denied
可以看到这个方法需要两个参数,一个是request,一个是message=getattr(permission, 'message', None),getattr()从permission中找message,没找到就设为Node
x
1
def permission_denied(self, request, message=None):
2
"""
3
If request is not permitted, determine what kind of exception to raise.
4
"""
5
if request.authenticators and not request.successful_authenticator:
6
raise exceptions.NotAuthenticated()
7
raise exceptions.PermissionDenied(detail=message)
permission_denied最终抛出了一个异常exceptions.PermissionDenied,这个异常接受了message,而它实际就是我们异常的提示语,所以,可以修改上面自定义的MyPermission类,不需要我们手动抛出异常,只要return False即可:
x
1
class MyPermission(BasePermission):
2
3
message = '需要会员用户才能访问'
4
5
def has_permission(self, request, view):
6
if not request.user:
7
return False
8
username = request.user
9
try:
10
group_name = User.objects.get(username=username).group.group_name
11
except User.DoesNotExist:
12
return False
13
if group_name == 'usual':
14
return False
15
return True
测试: