认证-Authentication

认证Authentication

1、认证的写法

#认证的实现
 -1.写一个类,继承BaseAuthentication,重写authenticate,认证的逻辑写在里面,返回两个值,一个值最终给了Request对象的user,,如果认证失败就抛异常:AuthenticationFailed
 -2.全局使用,局部使用

2、认证的源码分析

# 1. APIView-----》dispatch方法-----》self.initial(request, *args, **kwargs)----》有认证,权限,频率
# 2.  只读认证源码 initial-----》 self.perform_authentication(request)

    def perform_authentication(self, request):
     	#去Resquest类中查找,方法属性user的get方法
        request.user
        
# 3. self.perform_authentication(request)就一句话:   request.user ,需要去drf的Request对象中找user属性(方法)
    class Request:
        @property
        def user(self):
            if not hasattr(self, '_user'):
                with wrap_attributeerrors():
                    
                    self._authenticate()
                    
            return self._user

# 4. Request类中的user方法,刚开始,没有_user,所以走self._authenticate()
	    @property
    def user(self):
        if not hasattr(self, '_user'):
            with wrap_attributeerrors():
                self._authenticate()
        return self._user
    
# 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()

3、认证组件的使用

# 写一个认证类,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')


全局配置
#可以有多个认证,从左到右依次执行
#全局使用,在settings.py中配置
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        "app01.app_auth.MyAuthentication"
    ]
}

局部使用
#局部使用,在视图类上写
authentication_classes = [MyAuthentication]
#urls.py
from django.urls import path
from app01 import views
from rest_framework import routers

router = routers.SimpleRouter()
router.register(prefix='books', viewset=views.BookViewSet)
urlpatterns = [
    path('login/', views.LoginView.as_view()),
]
urlpatterns += router.urls


#views.py
from rest_framework.viewsets import ModelViewSet
from rest_framework.decorators import action  # 装饰器
from app01.models import Book
from app01.serializer import BookSerializer
from app01.app_auth import MyAuthentication

class BookViewSet(ModelViewSet):
    
    authentication_classes = [MyAuthentication]
    
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    @action(methods=['get'], detail=False)
    def get_1(self, request):
        book_obj = self.get_queryset()[:2]  # 从0开始截取一条
        ser = self.get_serializer(book_obj, many=True)
        return Response(ser.data)

 
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
import uuid

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:
            # 生成随机字符串
            res = uuid.uuid4()
            #存到usertoken表中
            # models.UserToken.objects.create(token=res,user=user)#用它每次登录都会存一条数据,不好,
            models.UserToken.objects.update_or_create(defaults={'token':res},user=user)
            return Response({'status': 100, 'msg': '登录成功', 'token':res})

        return Response({'status':101,'msg':'用户名或密码错误'})
局部禁用
#局部禁用
配置了全局后可以局部禁用
authentication_classes = []
class BookViewSet(ModelViewSet):
    authentication_classes = [MyAuthentication]
    queryset = Book.objects.all()
    serializer_class = BookSerializer

    @action(methods=['get','post'], detail=False)
    def get_1(self, request):
        book_obj = self.get_queryset()[:2]
        ser = self.get_serializer(book_obj, many=True)
        return Response(ser.data)


class LoginView(APIView):
    authentication_classes = []

    def post(self, request):
        username = request.data.get('username')
        password = request.data.get('password')
        user = User.objects.filter(username=username, password=password).first()
        if user:
            token = uuid.uuid4()
            UserToken.objects.update_or_create(defaults={'token': token}, user=user)
            return Response({'status': 100, 'msg': '登录成功', 'token': token})
        else:
            return Response({'status': 101, 'msg': '用户名或密码错误'})

image-20221209205742421

image-20221209205643076

posted @ 2022-12-12 17:26  ExpiredSaury  阅读(143)  评论(0编辑  收藏  举报