Django BaseAuthentication流程分析
目录
一、BaseAuthentication - 用于拦截请求,在视图函数钱执行相应认证方法
1-1 登陆相关视图函数 - 使用Token字符串存储数据库模拟session
1-2 BaseAuthentication 登陆认证 - drfAuth.py
二、认证配置 - authentication_classes
一、BaseAuthentication - 用于拦截请求,在视图函数钱执行相应认证方法
总结:
- 认证类
- 认证类必须单独存放在py文件内,若防止在view内,使用全局配置时候,无法使用
- 必须继承 BaseAuthentication
- from rest_framework.authentication import BaseAuthentication
- 认证类内必须重写 authenticate(self, request) 方法,request参数必须传入
- 若后续有其他认证类需要执行,必须返回空!!
- 若后续不无其他认证类需要执行,则返回两个值 - 对应DRF内Request对象User类内_authenticate方法执行
- 若认证失败,抛出 exceptions.APIException 异常
- from rest_framework import exceptions
- 执行认证的视图类中配置 authentication_classes = [drfAuth.LoginAuth, ]
1-1 登陆相关视图函数 - 使用Token字符串存储数据库模拟session
from rest_framework.views import APIView import hashlib, time from app01 import models from django.core.exceptions import ObjectDoesNotExist from django.http import JsonResponse def get_token(name): ''' 将当前时间戳转化为被hash的md5字符串 - 注意:md5.update(内部必须插入bytes格式) :param name: :return: ''' md5 = hashlib.md5() md5.update(str(time.time()).encode('utf-8')) md5.update(name.encode('utf-8')) return md5.hexdigest() class Login(APIView): ''' 接收对应请求发送的数据,对其中的name和pwd进行校验 - 校验通过:使用get_token(name)获取一个当前唯一的字符串作为token返回给前端,并且存入数据库中 - 校验不通过:返回错误信息 ''' def post(self, request, *args, **kwargs): response = {'status': 100, 'msg': '登陆成功'} name = request.data.get('name') pwd = request.data.get('pwd') try: # 使用get获取表内符合对象,如不存在则报错 user = models.UserInfo.objects.get(name=name, pwd=pwd) token = get_token(name) # update_or_create : 表内记录的更新或创建 models.UserToken.objects.update_or_create(user=user, defaults={'token': token}) response['token'] = token except ObjectDoesNotExist as e: response['status'] = 101 response['msg'] = '用户名密码错误' except Exception as e: # 未知错误捕获 response['status'] = 101 response['msg'] = str(e) return JsonResponse(response, safe=False)1-2 BaseAuthentication 登陆认证 - drfAuth.py
from rest_framework.authentication import BaseAuthentication from app01 import models from rest_framework import exceptions import datetime class LoginAuth(BaseAuthentication): # authenticate必须在DRF认证内被重写,request参数必须传入 def authenticate(self, request): ''' 将获取的token去token表内进行比对,存在信息即验证通过 - 获取token表内token更新时间,若超时则验证失败重新登陆(用于数据的清理) - 验证通过:返回空 - 表示后续仍能继续其他验证 = 验证通过:返回认证用户和当前数据记录对象 - 后续不再进行验证 - 对应DRF内Request对象User类内_authenticate方法执行 - from rest_framework.request import Request :param request: :return: ''' # 数据放在header内传输,request.META获取 # meta查询key值格式:HTTP_大写字段名 例如:token - HTTP_TOKEN token = request.META.get('HTTP_TOKEN') # token = request.query_params.get('token') print(token) ret = models.UserToken.objects.filter(token=token).first() if not ret: # 查不到,抛异常 raise exceptions.APIException('认证失败,请重新登陆!') t = datetime.datetime.now() - ret.token_time print(t.total_seconds()) # 若表内登陆时间超过10min则自动清除 if t.total_seconds() > 600: ret.delete() raise exceptions.APIException('登陆超时,请重新登陆!') # 查询到对应用户信息,认证通过 # 返回当前认证用户,当前token记录对象 # 返回的数据可通过 request.user, request.auth进行获取 return ret.user, ret1-3 视图函数
from rest_framework.views import APIView from django.http import JsonResponse from app01 import MySerializer from app01 import drfAuth class Books(APIView): # 列表中,类名不能加括号 authentication_classes = [drfAuth.LoginAuth, ] def get(self, request, *args, **kwargs): # 只要通过认证,就能取到当前登录用户对象 print(request.user) response = {'status': 100, 'msg': '查询成功'} ret = models.Book.objects.all() book_ser = MySerializer.BookSerializer(ret, many=True) response['data'] = book_ser.data return JsonResponse(response, safe=False)
二、认证配置 - authentication_classes
2-1 局部配置
''' 需认证类内配置 !! 注意:可以在列表中存入多个认证类,但是存在返回值的认证类必须位于最后!! ''' authentication_classes = [drfAuth.LoginAuth, ]2-2 全局配置 及 局部禁用
''' settings配置文件 所有视图内的类都会经过REST_FRAMEWORK内的认证类内认证 ''' REST_FRAMEWORK={ 'DEFAULT_AUTHENTICATION_CLASSES':['app01.drfAuth.LoginAuth',] } ''' 某一个视图类内禁用认证 - 认证规则首先使用当前类内的 authentication_classes 规则 - 置空表示不执行任何认证 ''' authentication_classes = []
三、相关源码分析
''' DRF内 Request对象 User类内 _authenticate方法 ''' def _authenticate(self): """ Attempt to authenticate the request using each authentication instance in turn. """ # 循环视图类内定义的所有认证类 for authenticator in self.authenticators: try: # 执行当前认证类,并且获取返回值 user_auth_tuple = authenticator.authenticate(self) # 若不存在返回值,抛出异常 except exceptions.APIException: self._not_authenticated() raise # 若认证类存在返回值,并且符合self.user, self.auth返回规则,return跳出循环 if user_auth_tuple is not None: self._authenticator = authenticator self.user, self.auth = user_auth_tuple return self._not_authenticated()