15 认证+认证源码解析

认证的写法

# 作用:校验用户是否登录,如果登录了,继续往后走,如果没有登录,直接返回

# 登录功能---》


# 认证的使用
    -第一步:写一个认证类,继承BaseAuthentication,重写authenticate  方法
    -第二步:在 authenticate 方法中判断用户是否登录(取出用户携带的token,去判断)
    -第三步:如果认证通过,返回两个值,如果认证不通过抛异常
        -# 在后续的request对象中,有这两个值,第一个给了request.user,第二个值给了request.auth
    -第四步:把写的认证类,配置在视图类中(跟请求和响应的配置一样),全局配置
    
    
    
# 写一个认证类,继承BaseAuthentication
    -鸭子类型:不显示的继承某个类,只要类中有共同的属性和方法,我们就属于同一类

要求所有的图书接口必须登录后才可以操作

#models.py

class User(models.Model):
    username=models.CharField(max_length=32)
    password=models.CharField(max_length=32)


class UserToken(models.Model):
    token=models.CharField(max_length=32)
    user=models.OneToOneField(to="User",on_delete=models.CASCADE)

class Book(models.Model):
    title=models.CharField(max_length=32)
    price=models.IntegerField()
    publish=models.CharField(max_length=32)

serializer.py

from rest_framework.serializers import ModelSerializer
from app01.models import Book
class BookSerializer(ModelSerializer):
    class Meta:
        model = Book
        fields="__all__"

views.py 局部配置


from django.shortcuts import render
from rest_framework.viewsets import ViewSet,ModelViewSet
from app01 import models
from rest_framework.response import Response
from rest_framework.decorators import action
import uuid
from app01.serializer import BookSerializer
from app01 import auth

class UserView(ViewSet):
authentication_classes = []

@action(methods=['POST'],detail=False)
def login(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=str(uuid.uuid4())
models.UserToken.objects.update_or_create(defaults={"token":token},user=user)
return Response({"code":100,"msg":"登录成功","token":token})
else:
return Response({"code":101,"msg":"用户名或密码不正确"})


#
局部使用,在视图类中使用 # 5个接口都需要登录以后才能访问 from .auth import LoginAuth class BookView(ModelViewSet): # authentication_classes = [LoginAuth,] # 登录认证 serializer_class = BookSerializer queryset = Book.objects.all()

auth.py

from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from .models import UserToken


class LoginAuth(BaseAuthentication):
    def authenticate(self, request):
        # token=request.query_params.get('token')  # get请求的地址中取
        # 请求头中取?
        # request.META   http请求头中的一些数据,HTTP_大写   HTTP_TOKEN
        token = request.META.get('HTTP_TOKEN')
        # 去数据库查询该token串是否存在,如果存在,取出对应的用户,就是当前登录用户
        user_token = UserToken.objects.all().filter(token=token).first()
        if user_token:
            # 这个用户登录了,放行
            return user_token.user, token  # 在后续的request对象中,有这两个值,第一个给了request.user,第二个值给了request.auth
        else:
            raise AuthenticationFailed('您没有登录')

urls.py

from django.urls import path, re_path,include
from app01 import views

from rest_framework.routers import SimpleRouter
router=SimpleRouter()
router.register('user',views.UserView,'user')
router.register('book',views.BookView,'book')

urlpatterns = [
    #    path('admin/', admin.site.urls),

]
urlpatterns+=router.urls

局部配置和全局配置

# 局部使用,在视图类中使用
# 5个接口都需要登录以后才能访问
from .auth import LoginAuth
class BookView(ModelViewSet):
    # authentication_classes = [LoginAuth,]  # 登录认证
    serializer_class = BookSerializer
    queryset = Book.objects.all()


    def list(self, request, *args, **kwargs):    #重写list
        # 认证通过以后,request.user
        print(request.user)  # 只要认证通过,进入到视图类中,这个就是当前登录用户(认证类返回的时候,必须给他)
        print(request.auth)
        return super().list(request, *args, **kwargs)

    
    
    
#全局使用,配置在配置文件中
REST_FRAMEWORK = {

    'DEFAULT_AUTHENTICATION_CLASSES': [  # 全局使用,所有接口都必须登录才能用
        'app01.auth.LoginAuth'
    ]

}

 

 在请求头中携带token

 

 

 

 

 

 

 

 

 

 

认证的源码分析

#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) #注意这self是request对象
            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()

认证组件的使用

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

# 可以有多个认证,从左到右依次执行
# 全局使用,在setting.py中配置
REST_FRAMEWORK={
    "DEFAULT_AUTHENTICATION_CLASSES":["app01.app_auth.MyAuthentication",]
}
# 局部使用,在视图类上写
authentication_classes=[MyAuthentication]
# 局部禁用
authentication_classes=[]

 

posted @ 2021-12-24 19:37  甜甜de微笑  阅读(84)  评论(0编辑  收藏  举报