Django Rest framework 之 权限 - weilanhanf

时间:2024-01-22 12:39:33

Django Rest framework 之 权限

一、权限实例

在阅读本文之前请先参考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或者Trueself.permission_denied(request, message=getattr(permission, \'message\', None))说明可以在权限类中重写message属性,来定义权限不通过时候的提示信息。

进入self.get_permissions()来看一下

4、获取所有的权限类

在APIView中有定义默认的权限类,因此也可以通过全局配置的方法配置权限类。

5、原生的权限类

像认证那样,django rest framework中也有权限类

可以根据自己的需要调用。

三、总结

权限其实的流程跟之前的认证流程是一样的,认证类封装到request中,然后再调用认证类的方法,不过这里的方法返回值不再是像认证组件那样的直接返回一个认证的对象,而是返回一个True或者False值表示认证过的对象是否有某些权限再进行具体操作。
这里注意的是,在自己重写权限类的相关方法,添加自己的逻辑的时候,返回值需要是一个布尔值,Flase或者True,表示是否有权限。也可以通过全局配置和局部配置权限类。