认证-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': '用户名或密码错误'})
本文来自博客园,作者:ExpiredSaury,转载请注明原文链接:https://www.cnblogs.com/saury/p/16976693.html