博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
DRF(4) - 认证、权限组件
阅读量:2240 次
发布时间:2019-05-09

本文共 7071 字,大约阅读时间需要 23 分钟。

一、引入

  通过前面三节课的学习,我们已经详细了解了DRF提供的几个重要的工具,DRF充分利用了面向对象编程的思想,对Django的View类进行了继承,并封装了其as_view方法和dispatch方法,随后提供了几个非常方便的编程工具,比如解析器、序列化。

  我们通过解析器,可以对来自客户端的application/json数据进行解析,另外,通过序列化工具,我们能够快速构建一套符合REST规范的api,随后又通过DRF的mixin、view及viewset对这些接口逻辑进行优化。

  有了他们,程序员开发Web应用的效率大大提高了,虽然我们也尝试自己动手实现了这些功能,但是既然有了优秀的工具,我们就不必费尽心思重复发明轮子。DRF并不仅仅提供了这几个工具,今天我们就来继续深入学习DRF提供的一些其他的工具。

  跟以往一样,我们不仅仅要学会这些工具的使用方式,并且要深入研究他们的源码,希望大家在研究源码的过程中,能够对面向对象编程的思路有更加深刻的认识。

  在编程的世界中,我们认为,用户输入的所有数据都是不可靠的,不合法的,直接使用用户输入的数据是不安全的,接下来,我们要学习认证组件、权限组件、频率组件。

二、认证组件

  很久以前,Web站点只是作为浏览文档和其他资源的工具,甚少有什么用户交互之类的烦人的事情需要处理,所以,Web站点的开发这根本不关心什么人在什么时候访问了什么资源,不需要记录任何数据,有客户端请求,我即返回数据,简单方便,每一个http请求都是新的,响应之后立即断开连接。

  而如今,不管是论坛类、商城类、社交类、门户类还是其他各类Web站点,大家都非常重视用户交互,只有跟用户交互了,才能进一步留住用户,只有留住了用户,才能知道用户需求,知道了用户需求,就是有商机,有了流量,才能够有钱,有了资金企业才能继续发展,可见,用户交互是非常重要的,甚至可以说是至关重要的一个基础功能。而谈到用户交互,必须要谈到下面介绍的认证、权限和频率。

  http协议是无状态的,大家都知道,之前我们学习过使用cookie和session两种方式可以保存用户信息,这两种方式不同的是cookie保存在客户端浏览器中,而session保存在服务器中,他们各有优缺点,配合起来使用,可将重要的敏感的信息存储在session中,而在cookie中可以存储不太敏感的数据。

  下面介绍的是使用token的方式,token称之为令牌。cookie、session和token都有其应用场景,没有谁好谁坏,不过我们开发数据接口类的Web应用,目前用token还是比较多的。

  token认证的大致步骤是这样的:

    - 用户登录,获取用户名密码,查询用户表,如果存在该用户,生成token,否则返回错误信息;

    - 更新或者新增token信息;

  接下来,我们创建两个model,如下所示(token也可以存储在user表中,不过建议存储在userToken表中):

from django.db import modelsclass User(models.Model):    username = models.CharField(max_length=32)    password = models.CharField(max_length=32)    user_type_entry = (        (1, 'Delux'),        (2, 'SVIP'),        (3, 'VVIP')    )    user_type = models.IntegerField(choices=user_type_entry)    def __str__(self):        return self.usernameclass UserToken(models.Model):    token =models.CharField(max_length=128)    user = models.OneToOneField(to='User', on_delete=models.CASCADE)

  我们无需实现get方法,因为涉及登录认证,所以写post方法接口,登录都是post请求,登录视图类如下所示:

class UserView(APIView): def post(self, request):        fields = {
'username', 'password'} user_info = dict() if fields.issubset(set(request.data.keys())): for key in fields: user_info[key] = request.data[key] user_instance = User.objects.filter(**user_info).first() response = dict() if user_instance is not None: access_token = get_token.generater_token() # 检查是否有此记录,有则更新,无则新增 UserToken.objects.update_or_create(user=user_instance, defaults={ "token": access_token }) response["status_code"] = 200 response["status_message"] = "登录成功" response["access_token"] = access_token response["user_role"] = user_instance.get_user_type_display() else: response["status_code"] = 201 response["status_message"] = "登录失败,用户名或密码错误" return JsonResponse(response)

  简单写了个获取随机字符串的方法用来生成token值,如下:

import uuiddef generater_token():    random_str = ''.join(str(uuid.uuid4()).split('-'))    return random_str

  以上就是token的简单生成方式,当然,在生产环境中不会如此简单,关于token也有相关的库,我们构造几条数据之后,可以通过POSTMAN工具来创建几个用户的token信息。

  接下来,如何对已经登录成功的用户实现访问授权呢?也就是说,只有登录过的用户(有token值)才能访问特定的数据,该DRF的认证组件出场了。

1、DRF认证组件使用

  首先,我们来看一看,DRF认证组件的使用方式,首先,我们必须新建一个认证类,之后的认证逻辑就包含在这个类里面:

from rest_framework.exceptions import AuthenticationFailed# 定义认证类class UserAuth(object):    # 固定写法(继承BaseAuthentication的话,authenticate_header可以不写)    def authenticate_header(self, request):           pass    def authenticate(self, request):        # 使用postman模拟发送token,所以用request.query_params        user_post_token = request.query_params.get('token')        token_object = UserToken.objects.filter(token=user_post_token).first()        if token_object:            return token_object.user, token_object.token        else:            raise AuthenticationFailed("认证失败")

  实现方式看上去非常简单,到token表里面查看token是否存在,然后根据这个信息,返回对应信息即可,然后,在需要认证通过才能访问的数据接口里面注册认证类即可:

from rest_framework.viewsets import ModelViewSetclass BookView(ModelViewSet):    authentication_classes = [UserAuth]  # 指定认证类     queryset = Book.objects.all()    serializer_class = BookSerializer

2、多个认证类的实现

  我们还可以指定多个认证类,但是需要注意的是,如果需要返回什么数据,必须在最后一个认证类中返回,因为如果在前面返回,在self._authentication()方法中会对返回值进行判断,如果不为空,认证的过程就会中止,多个认证类的实现方式如下:

class UserAuth(object):def authenticate_header(self, request):        pass    def authenticate(self, request):        raise AuthenticationFailed("认证失败")class UserAuth2(object):    def authenticate_header(self, request):        pass    def authenticate(self, request):        user_post_token = request.query_params.get('token')        token_object = UserToken.objects.filter(token=user_post_token).first()        if token_object:            return token_object.user, token_object.token        else:            raise AuthenticationFailed("认证失败")class BookView(ModelViewSet):    authentication_classes = [UserAuth, UserAuth2]

4、如果不希望每次都写那个无用的authenticate_header方法,我们可以这样:

from rest_framework.authentication import BaseAuthenticationfrom rest_framework.exceptions import AuthenticationFailedclass UserAuth2(BaseAuthentication):    def authenticate(self, request):        raise APIException("认证失败")class UserAuth(BaseAuthentication):    def authenticate(self, request):        user_post_token = request.query_params.get('token')        token_object = UserToken.objects.filter(token=user_post_token).first()        if token_object:            return token_object.user, token_object.token        else:            raise AuthenticationFailed("认证失败")

  继承BaseAuthentication类即可

5、全局认证

  如果希望所有的数据接口都需要认证怎么办?很简单,关键点就是下面这句源码:

  authentication_classes=api_settings.DEFAULT_AUTHENTICATION_CLASSES

  如果认证类自己没有authentication_classes,就会到settings中去找,通过这个机制,我们可以将认证类写入到settings文件中即可实现全局认证:

REST_FRAMEWORK = {    'DEFAULT_AUTHENTICATION_CLASSES': (        'authenticator.utils.authentication.UserAuth',        'authenticator.utils.authentication.UserAuth2',    ),}

三、权限组件

1、权限组件使用

  1)定义权限类

class UserPerms():  message = '您没有访问权限'   # 重写提示信息  # 当url中含有正则时要写has_object_permission方法  def has_object_permission(self, request, view, obj):    return True  def has_permission(self, request, view):    if request.user.user_type == 2:      return True    else:      return False

  2)同样的逻辑,同样的方式,只是执行权限的方法名与执行认证的方法名不一样而已,名为has_permission,并且需要将当前的视图类传递给该方法。在视图类中加入permission_classes变量:

from rest_framework.viewsets import ModelViewSetclass BookView(ModelViewSet):    authentication_classes = [UserAuth]  # 指定认证类    permission_classes = [UserPerms]   # 指定权限类    queryset = Book.objects.all()    serializer_class = BookSerializer

四、补充知识点

1、issubset是否是子集

fields = {
'username', 'password'}user_data = {
'username':'alex', 'password':'123', 'hobby':'sing'}print(fields.issubset(user_data.keys())) # True

2、给属性设置值

class Person(object):    @property    def name(self):        print('getter has been executed')        return self._name    @name.setter    def name(self,value):        print('setter has been executed')        self._name = valuep1 = Person()p1.name = 'alex'print(p1.name)

3、update_or_create

# 检查是否有这条记录,有则更新(需要defaults参数,类型字典),无则新增UserToken.objects.update_or_create(user=user_obj, defaults={    "token": access_token})

 

转载于:https://www.cnblogs.com/li-li/p/10098412.html

你可能感兴趣的文章
【Java】【28】提高List的removeAll方法的效率
查看>>
【JS】【31】读取json文件
查看>>
OpenSSL源代码学习[转]
查看>>
google app api相关(商用)
查看>>
linux放音乐cd
查看>>
GridView+存储过程实现'真分页'
查看>>
flask_migrate
查看>>
解决activemq多消费者并发处理
查看>>
UDP连接和TCP连接的异同
查看>>
hibernate 时间段查询
查看>>
java操作cookie 实现两周内自动登录
查看>>
Tomcat 7优化前及优化后的性能对比
查看>>
Java Guava中的函数式编程讲解
查看>>
Eclipse Memory Analyzer 使用技巧
查看>>
tomcat连接超时
查看>>
谈谈编程思想
查看>>
iOS MapKit导航及地理转码辅助类
查看>>
检测iOS的网络可用性并打开网络设置
查看>>
简单封装FMDB操作sqlite的模板
查看>>
iOS开发中Instruments的用法
查看>>