zoukankan      html  css  js  c++  java
  • drf-认证

    1 认证Authentication

    1.1 自定义认证方案

    1.1.1 编写models

    models.py

    from django.db import models
    
    
    # Create your models here.
    class Book(models.Model):
        name = models.CharField(max_length=32)
        price = models.DecimalField(max_digits=8, decimal_places=2)
        publish = models.CharField(max_length=32)
    
    # ===========================认证用到的============================
    class User(models.Model):
        username = models.CharField(max_length=32)
        password = models.CharField(max_length=32)
        user_type = models.IntegerField(choices=((1, '超级用户'), (2, '普通用户'), (3, '二笔用户')))
    
    
    class UserToken(models.Model):
        token = models.CharField(max_length=64)
        user = models.OneToOneField(to='User', on_delete=models.CASCADE)   # on_delete=models.CASCADE级联删除

    1.1.2 新建自定义认证类

    app01/app_auth.py

    from rest_framework.authentication import BaseAuthentication
    from rest_framework.exceptions import AuthenticationFailed
    from app01.models import UserToken
    
    
    class MyAuthentication(BaseAuthentication):
        def authenticate(self, request):
            # 认证逻辑,如果认证通过,返回两个值
            # 如果认证失败,抛出AuthenticationFailed异常
            token = request.GET.get('token')
            if token:
                user_token = UserToken.objects.filter(token=token).first()
                # 认证通过
                if user_token:
                    return user_token.user, token
                else:
                    raise AuthenticationFailed('认证失败')
            else:
                raise AuthenticationFailed('请求地址中需要携带token')

    1.1.3 编写视图

    views.py

    from rest_framework.viewsets import ModelViewSet
    from rest_framework.views import APIView
    from app01.models import Book
    from app01.serializers import BookSerializer
    from rest_framework.decorators import action  # 装饰器
    from rest_framework.response import Response
    # ===============认证相关===========================
    from app01.app_atuh import MyAuthentication
    from app01 import models
    import uuid
    
    # Create your views here.
    class BookViewSet(ModelViewSet):
        authentication_classes = [MyAuthentication, ]     # 局部使用认证
        queryset = Book.objects.all()
        serializer_class = BookSerializer
        @action(methods=['GET', 'POST'], detail=True)
        def get_1(self, request, pk):
            print(pk)
            book = self.get_queryset()[:1]  # 从0开始截取一条
            ser = self.get_serializer(book, many=True)
            return Response(ser.data)
    
    class LoginView(APIView):
        def post(self, request):
            username = request.data.get("username")
            password = request.data.get("password")
            user = models.User.objects.filter(username=username, password=password).first()
            if user:
                # 登录成功,生成随机字符串
                token = uuid.uuid4()
                # 存到UserToken表中
                # models.UserToken.objects.create(token=u_str, user=user)   # 用它每次登录都会记录一条,不好,
                # update_or_create有就更新,没有就新增
                models.UserToken.objects.update_or_create(defaults={'token': token}, user=user)
                return Response({'status': 200, 'msg': '登录成功', 'token': token})
            else:
                return Response({'status': 101, 'msg': '用户名或密码错误'})

    1.1.4 路由

    urls.py

    from django.contrib import admin
    from django.urls import path, re_path
    from app01 import views
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('login/', views.LoginView.as_view()),
    ]
    
    
    from rest_framework import routers
    router = routers.SimpleRouter()
    router.register('books', views.BookViewSet)  # 不要加斜杠了
    
    urlpatterns += router.urls

    1.1.5 全局使用(在项目settings.py配置文件中配置)

    REST_FRAMEWORK={
        "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
    }

    1.1.6 局部使用

    #局部使用,只需要在视图类里加入:
    authentication_classes = [TokenAuth, ]

     1.2 认证源码分析

    认证的写法:

    # 认证的实现
        1 写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,认证通过,返回两个值,一个值最终给了Requet对象的user,认证失败,抛异常:APIException或者AuthenticationFailed
        2 全局使用,局部使用

    源码分析

    #1 APIVIew----》dispatch方法---》self.initial(request, *args, **kwargs)---->有认证,权限,频率
    #2 只读认证源码: self.perform_authentication(request)
    #3 self.perform_authentication(request)就一句话:request.user,需要去drf的Request对象中找user属性(方法) 
    #4 Request类中的user方法,刚开始来,没有_user,走 self._authenticate()
    
    #5 核心,就是Request类的 _authenticate(self):
        def _authenticate(self):
            # 遍历拿到一个个认证器,进行认证
            # self.authenticators配置的一堆认证类产生的认证类对象组成的 list
            #self.authenticators 你在视图类中配置的一个个的认证类:authentication_classes=[认证类1,认证类2],对象的列表
            for authenticator in self.authenticators:
                try:
                    # 认证器(对象)调用认证方法authenticate(认证类对象self, request请求对象)
                    # 返回值:登陆的用户与认证的信息组成的 tuple
                    # 该方法被try包裹,代表该方法会抛异常,抛异常就代表认证失败
                    user_auth_tuple = authenticator.authenticate(self)
                except exceptions.APIException:
                    self._not_authenticated()
                    raise
    
                # 返回值的处理
                if user_auth_tuple is not None:
                    self._authenticator = authenticator
                    # 如何有返回值,就将 登陆用户 与 登陆认证 分别保存到 request.user、request.auth
                    self.user, self.auth = user_auth_tuple
                    return
            # 如果返回值user_auth_tuple为空,代表认证通过,但是没有 登陆用户 与 登陆认证信息,代表游客
            self._not_authenticated()
  • 相关阅读:
    ExtAspNet新版本发布v2.0beta4
    自己实现Rich Text Editor
    ExtAspNet应用技巧(五) 动态创建工具栏菜单
    IronPython的Hello World
    关于“跨语言调用”和“CLS(公共语言规范)”的一点体会
    文档,又是文档
    对项目目标的一点想法
    推荐一个WMI的查询分析器
    几个很不错的.NET 相关的FAQ和例子代码的连接
    用IronPython写winform程序
  • 原文地址:https://www.cnblogs.com/baicai37/p/13277207.html
Copyright © 2011-2022 走看看