zoukankan      html  css  js  c++  java
  • DRF Django REST framework 之 认证与权限组件(五)

    引言

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

    而如今,互联网的世界发生了翻天覆地的变化,用户不仅仅需要跟其他用户沟通交流,还需要跟服务器交互,不管是论坛类、商城类、社交类、门户类还是其他各类Web站点,大家都非常重视用户交互,只有跟用户交互了,才能进一步留住用户,只有留住了用户,才能知道用户需求,知道了用户需求,才会产生商机,有了用户,就等于有了流量,才能够骗到…额…是融到钱,有了资金企业才能继续发展,可见,用户交互是非常重要的,甚至可以说是至关重要的一个基础功能。

    而谈到用户交互,则必须要谈到我们今天所要学习的知识点,认证、权限和频率。首先我们来看看认证。

    认证组件

    使用token

    大部分人都知道cookie和session这两种方式可以保存用户信息,这两种方式不同的是cookie保存在客户端浏览器中,而session保存在服务器中,他们各有优缺点,配合起来使用,可将重要的敏感的信息存储在session中,而在cookie中可以存储不太敏感的数据。

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

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

    • 用户登录,服务器端获取用户名密码,查询用户表,如果存在该用户且第一次登录(或者token过期),生成token,否则返回错误信息
    • 如果不是第一次登录,且token未过期,更新token值

    定义 url 

    from django.urls import path, re_path
    
    from DrfOne import views
    
    urlpatterns = [
    
        path("books/", views.BookView.as_view({
            "get": "list",
            "post": "create",
        })),
        re_path('books/(?P<pk>d+)/', views.BookView.as_view({
            'get': 'retrieve',
            'put': 'update',
            'delete': 'destroy'
        })),
    
        # 登陆
        path('login/', views.LoginView.as_view()),
    ]

    创建两个 model ,如下所示:

    from django.db import models
    
    class UserInfo(models.Model): username = models.CharField("姓名", max_length=32) password = models.CharField("密码", max_length=32) age = models.IntegerField("年龄") gender = models.SmallIntegerField("性别", choices=((1, ""), (2, "")), default=1) user_type_entry = ((1, "普通用户"), (2, "VIP"), (3, "SVIP")) user_type = models.SmallIntegerField("用户级别", choices=user_type_entry) def __str__(self): return self.username class UserToken(models.Model): user = models.ForeignKey(to="UserInfo", on_delete=models.CASCADE) token = models.CharField(max_length=128)

     post 方法接口,视图类如下所示:

    from django.http import JsonResponse
    
    from rest_framework.views import APIView
    
    from DrfTwo.models import UserInfo, UserToken
    
    
    class LoginView(APIView):
        def post(self, request):
            # 定义返回信息
            ret = dict()
            try:
                # 定义需要的信息
                fields = {"username", "password"}
                # 定义一个用户信息字典
                user_info = dict()
                # 判断fields是否是request.data的子集
                if fields.issubset(set(request.data.keys())):
                    for key in fields:
                        user_info[key] = request.data.get(key)
    
                user_instance = UserInfo.objects.filter(**user_info).first()
                # 用户验证
                if user_instance:
                    # 自定义generate_token()方法,获取token值,在后面
                    access_token = generate_token()
                    # 用户登陆成功,创建token,token存在更新token, defaults为更新内容
                    UserToken.objects.update_or_create(user=user_instance, defaults={
                        "token": access_token
                    })
                    ret["status_code"] = 200
                    ret["status_message"] = "登录成功"
                    ret["access_token"] = access_token
                    ret["user_role"] = user_instance.get_user_type_display()
    
                else:
                    ret["status_code"] = 201
                    ret["status_message"] = "登录失败,用户名或密码错误"
            except Exception as e:
                ret["status_code"] = 202
                ret["status_message"] = str(e)
    
    
            return JsonResponse(ret)

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

    import uuid
    
    
    def generate_token():
        random_str = str(uuid.uuid4()).replace("-", "")
        return random_str

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

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

    认证组件的使用

    首先,新建一个认证类,之后的认证逻辑就包含在这个类里面:

    # 1.定义认证类
    class UserAuth(object):
        # 认证逻辑
        def authenticate(self, request):
            user_token = request.GET.get('token')
    
            token_object = UserToken.objects.filter(token=user_token).first()
            if token_object:
                return token_object.user, token_object.token
            else:
                raise APIException("认证失败")

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

    from rest_framework.viewsets import ModelViewSet
    from rest_framework.exceptions import APIException
    
    from DrfOne.models import Book, UserToken
    from DrfOne.drf_serializers import BookSerializer
    
    
    # 1.定义认证类
    class UserAuth(object):
        # 认证逻辑
        def authenticate(self, request):
            user_token = request.GET.get('token')
    
            token_object = UserToken.objects.filter(token=user_token).first()
            if token_object:
                return token_object.user.username, token_object.token
            else:
                raise APIException("认证失败")
    
    
    class BookView(ModelViewSet):
        # 2.指定认证类,固定写法
        authentication_classes = [UserAuth]
        # 获取数据源, 固定写法
        queryset = Book.objects.all()
        # 序列化类, 固定写法
        serializer_class = BookSerializer

    序列类 BookSerializer 

    from rest_framework import serializers
    
    from DrfOne import models
    
    
    class BookSerializer(serializers.ModelSerializer):
        class Meta:
            model = models.Book
            fields = "__all__"
    
            extra_kwargs = {
                # 仅写
                "publish": {'write_only': True},
                "authors": {'write_only': True},
            }
    
        publish_name = serializers.CharField(max_length=32, read_only=True, source="publish.name")
        publish_address = serializers.CharField(max_length=32, read_only=True, source="publish.address")
        author_name = serializers.SerializerMethodField()
    
        def get_author_name(self, book_obj):
            author_list = list()
            for author in book_obj.authors.all():
                # 注意列表添加字段,author.name而不是author
                author_list.append(author.name)
            return author_list
    类BookSerializer

    多个认证类

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

    # 1.定义认证类
    class UserAuth(object):
        # 认证逻辑
        def authenticate(self, request):
            user_token = request.GET.get('token')
    
            token_object = UserToken.objects.filter(token=user_token).first()
            if token_object:
                return token_object.user.username, token_object.token
            else:
                raise APIException("认证失败")
    
    
    class UserAuthTwo(object):
        def authenticate(self, request):
            raise APIException("就是这么简单!")
    
    
    class BookView(ModelViewSet):
        # 2.指定认证类,固定写法
        authentication_classes = [UserAuth, UserAuthTwo]
        # 获取数据源, 固定写法
        queryset = models.Book.objects.all()
        # 序列化类, 固定写法
        serializer_class = BookSerializer

    全局认证

    如果希望所有的数据接口都需要认证怎么办?很简单,如果认证类自己没有 authentication_classes ,就会到 settings 中去找,通过这个机制,我们可以将认证类写入到 settings 文件中即可实现全局认证:

    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'authenticator.views.UserAuth',
            'authenticator.views.UserAuthTwo',
        ),
    }

    权限组件

    与认证组件几乎差不多,只要判断该用户的级别,是否给予通过

    权限组件的使用

    定义权限类

    from rest_framework.permissions import BasePermission
    
    
    class UserPermission(BasePermission):
        message = "您没有权限访问该数据"
        def has_permission(self, request, view):
            # 用户级别是否超过普通用户
            if request.user.user_type > 1:
                return Truereturn False

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

    视图类中加入 permission_classes 变量:

    class BookView(ModelViewSet):
        # 指定认证类,固定写法
        authentication_classes = [UserAuth]
        # 指定权限类,固定写法
        permission_classes = [UserPermission]
        # 获取数据源, 固定写法
        queryset = models.Book.objects.all()
        # 序列化类, 固定写法
        serializer_class = BookSerializer

    ~>.<~

  • 相关阅读:
    android中获取某段程序的执行时间
    图像位宽对齐
    使用 ssh 连接github的方法说明(gitub的官方说法)
    转:程序员必须知道的几个Git代码托管平台
    转:webRTC的前世今生
    Eclipse c++头文件问题(未完)
    [原创] NetBean开发c++程序指南1- 加入c++项目文件夹
    xshell5 启动显示 mfc110.dll msvcp110.dll 未找到问题 解决办法
    vmware12安装vmtools
    转: EclipseIDE开发 for C++
  • 原文地址:https://www.cnblogs.com/pungchur/p/12028381.html
Copyright © 2011-2022 走看看