一、权限实例
在阅读本文之前请先参考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
,表示是否有权限。也可以通过全局配置和局部配置权限类。
各版本JDK1.5-1.8新特性
数据库(分库分表)中间件对比
使用IntelliJ IDEA创建Maven多模块项目
深入解析淘宝Diamond之客户端架构
淘宝Diamond架构分析
apktool 工具
ubuntu jdk 1.7 安装
dot 使用笔记
Chomsky_hierarchy