一, 权限实例
在阅读本文之前请先参考 django REST framework 之 认证中关于 django REST framework 的相关内容及实例
1, 目录结构
为了更好的管理各个功能组件, 在 django REST framework 之 认证中我们说到可以将认证类单独的拿出来, 放到其他目录下, 然后导入到 views.py 文件中, 在权限环节我们亦可以这么做, 目录结构就变成这样
在 API 这个 App 下创建一个 utils 包专门用来存放相关的组件.
2, 为模型类添加认证字段
我们在 models.py 中定义了两个模型类, 分别是
- from django.db import models
- class UserInfo(models.Model):
- USER_TYPE = (
- (1,'普通用户'),
- (2,'VIP'),
- (3,'SVIP')
- )
- user_type = models.IntegerField(choices=USER_TYPE, default=1)
- username = models.CharField(max_length=32)
- password = models.CharField(max_length=64)
- class UserToken(models.Model):
- user = models.OneToOneField(UserInfo,on_delete=models.CASCADE)
- token = models.CharField(max_length=64)
在 UserInfo 中通过为用户添加一个 user_type 字段来保证用户的身份, 是普通用户, VIP 还是 SVIP, 这样就可以通过用户的身份验证不同的权限. 如果想要定义一个视图类, 这个类中的逻辑只有超级用户才能访问.
3, 具体权限认证
可以再 utils 中的 permissions.py 中这么写
- # utils/permission.py
- class SVIPPremission(object):
- message = "必须是 SVIP 才能访问" # 这里的 message 表示如果不通过权限的时候, 错误提示信息
- def has_permission(self,request,view):
- if request.user.user_type != 3:
- return False
- return True
- class MyPremission(object):
- # 这个权限类表示当用户为 SVIP 时不可通过
- def has_permission(self,request,view):
- if request.user.user_type == 3:
- return False
- return True
这里只是判断用户的 USER_TYPE 的字段, 判断用户是否有权限, 也可以添加其他的逻辑进行判断.
4, 全局配置
在上一节的 django REST framework 之 认证的认证中, 将认证类放到了 settings.py 文件中, 这样会作用到视图中的每一个视图类, 如果视图类想要自己进行认证, 只需要重写 authentication_classes 即可, 那么对于权限来说我们也可以这么做,
- REST_FRAMEWORK = {
- 'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.authenticate.FirstAuthenticate', 'api.utils.authenticate.MyAuthenticate'],
- "UNAUTHENTICATED_USER": None, # 匿名, request.user = None
- "UNAUTHENTICATED_TOKEN": None,# 匿名, request.auth = None
- "DEFAULT_PERMISSION_CLASSES": ['api.utils.permission.MyPermission'], # 表示每一个视图类 (只要不重写 permission_classes 属性), 都需要 SVIP 的用户才能访问.
- }
5, 视图
在视图 view.py 中定义一个用户详情类 UserInfoView 作为测试, 这里的视图和上一节的 django REST framework 之 认证是相接的.
- from django.shortcuts import render, HttpResponse
- from django.http import JsonResponse
- from django.views import View
- from rest_framework.views import APIView
- from rest_framework.request import Request
- from .utils import authenticate, permission
- from API import models
- import JSON
- def md5(user):
- import hashlib
- import time
- # 当前时间, 相当于生成一个随机的字符串
- ctime = str(time.time())
- # token 加密
- m = hashlib.md5(bytes(user, encoding='utf-8'))
- m.update(bytes(ctime, encoding='utf-8'))
- return m.hexdigest()
- class AuthView(APIView):
- '''用于用户登录验证'''
- authentication_classes = [] #里面为空, 代表不需要认证
- permission_classes = [] #不里面为空, 代表不需要权限
- def get(self, request, *args, **kwargs):
- ret = {'code': 1000, 'msg': 'success', 'name': '偷偷'}
- ret = JSON.dumps(ret, ensure_ascii=False)
- return HttpResponse(ret)
- def post(self,request,*args,**kwargs):
- ret = {'code':1000,'msg':None}
- try:
- user = request.POST.get('username')
- pwd = request.POST.get('password')
- print(user, pwd)
- obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
- print(obj.username, obj.password)
- if not obj:
- ret['code'] = 1001
- ret['msg'] = '用户名或密码错误'
- #为用户创建 token
- token = md5(user)
- #存在就更新, 不存在就创建
- models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
- ret['token'] = token
- except Exception as e:
- ret['code'] = 1002
- ret['msg'] = '请求异常'
- return JsonResponse(ret)
- ORDER_DICT = {
- 1:{
- 'name':'apple',
- 'price':15
- },
- 2:{
- 'name':'狗子',
- 'price':100
- }
- }
- class OrderView(APIView):
- # 用户想要获取订单, 就要先通过身份认证, 在全局 settings.py 中已经配置
- permission_classes = []
- def get(self, request, *args, **kwargs):
- ret = {
- 'code': 1024,
- 'msg': '订单获取成功',
- }
- try:
- ret['data'] = ORDER_DICT
- except Exception as e:
- pass
- return JsonResponse(ret)
- class UserInfoView(APIView):
- permission_classes = [permission.SVIPPermission]
- def get(self, request, *args, **kwargs):
- print(request.user)
- return HttpResponse('SVIP 用户信息')
这里的 UserInfoView 重写了 permission_classes 属性, 则不会再使用 settings.py 中关于认证类的配置. 表示只有 SVIP 才能访问这个类的内部.
6, 路由分发
在 url.py 中设置路由分发
- from django.conf.urls import url
- from API.views import AuthView, OrderView, UserInfoView
- urlpatterns = [
- url(r'^api/v1/auth/$', AuthView.as_view()),
- url(r'^api/v1/order/$', OrderView.as_view()),
- url(r'^api/v1/info/$', UserInfoView.as_view()),
- ]
7, 请求测试
Postman 或者浏览器发送请求, 由于我们在 setting.py 中配置了'DEFAULT_AUTHENTICATION_CLASSES': ['api.utils.authenticate.FirstAuthenticate', 'api.utils.authenticate.MyAuthenticate'],
会对请求进行认证, 所以要带这用户的 token 才能通过, 进入到权限组件.
进入 SQLite 数据库, 找到对应的注册用户
在数据库中, 拿到刷新的用户的最近一次的 token
拿到这个 token 发送请求
请求成功, 则说明权限生效, 也可以在将 UserInfoView 改成如下
- class UserInfoView(APIView):
- permission_classes = [permission.SVIPPermission]
- def get(self, request, *args, **kwargs):
- print(request.user)
- return HttpResponse('SVIP 用户信息')
在发送请求, 就会发现用户不会有权限的提示信息. 如下
二, 源码分析
像 django REST framework 之 认证一样进入, request 的请求流程, 进入源码查看具体权限的操作
1, 进入 dispath() 方法
2, 进入 initial() 方法
3, 进入 check_permissions() 方法
4, 权限类的具体操作
在这里可以看到和认证中有类似的操作, 获取所有的权限类, 并且执行每一个权限类的 has_permission() 方法, 而这个方法具体封装了我们的判断权限操作, 但是 has_permission() 方法的返回值需要时 False 或者 True, self.permission_denied(request, message=getattr(permission, 'message', None)) 说明可以在权限类中重写 message 属性, 来定义权限不通过时候的提示信息.
进入 self.get_permissions() 来看一下
4, 获取所有的权限类
在 APIView 中有定义默认的权限类, 因此也可以通过全局配置的方法配置权限类.
5, 原生的权限类
像认证那样,
django REST framework
中也有权限类
可以根据自己的需要调用.
三, 总结
权限其实的流程跟之前的认证流程是一样的, 认证类封装到 request 中, 然后再调用认证类的方法, 不过这里的方法返回值不再是像认证组件那样的直接返回一个认证的对象, 而是返回一个 True 或者 False 值表示认证过的对象是否有某些权限再进行具体操作.
这里注意的是, 在自己重写权限类的相关方法, 添加自己的逻辑的时候, 返回值需要是一个布尔值, Flase 或者 True, 表示是否有权限. 也可以通过全局配置和局部配置权限类.
来源: https://www.cnblogs.com/welan/p/10127146.html