Django Rest Framework(二) 之用户认证及源码流程
全局认证和局部认证及源码流程和内部认证
自定义局部认证类
models.py
from django.db import models class UserInfo(models.Model): username = models.CharField(max_length=32,unique=True) password = models.CharField(max_length=64) user_type_choices=( (1,'普通用户'), (2,'vip用户'), (3,'超级用户'), ) user_type = models.IntegerField(choices=user_type_choices) class UserToken(models.Model): user = models.OneToOneField("UserInfo") token = models.CharField(max_length=64)
views.py
from django.shortcuts import render,HttpResponse from django.http import JsonResponse from rest_framework.views import APIView from rest_framework.request import Request from api_test import models import time,hashlib from rest_framework import exceptions
from rest_framework.authentication import BaseAuthentication #测试数据 order_dict = { 1:{ "name":"apple", 'price':'15', 'addr':'bj', 'date':'2018-06-01' }, 2:{ "name":"tomato", 'price':'5', 'addr':'bj', 'date':'2018-06-01' } } #生成token的函数 def md5(user): ctime = str(time.time()) m = hashlib.md5(bytes(user,encoding="utf-8")) m.update(bytes(ctime,encoding="utf-8")) return m.hexdigest() class AuthView(APIView): ''' 用户登录认证 ''' def post(self,request,*args,**kwargs): ret = {'code':10000,'msg':None} #返回的状态码和相关信息设置 try: user = request._request.POST.get("username") #因为这个类继承的Apiview,经过处理后的request(不是原生的),需要通过request._request才能获取原生request内的值,具体看Request函数(上一篇文章) pwd = request._request.POST.get("password") obj = models.UserInfo.objects.filter(username=user,password=pwd).first() #验证用户名和密码 if not obj: ret['code'] = 10001 ret['msg'] = "用户名密码错误"
#如果用户不存在,则创建token,如果用户存在,则更新token token = md5(user) models.UserToken.objects.update_or_create(user=obj,defaults={'token':token}) #更新或者创建的方法 ret['token'] = token except Exception as e: ret['code'] = 10002 ret['msg'] = "请求异常" return JsonResponse(ret)
#自定义用户认证类 class Authication(BaseAuthentication): #用户认证的函数名称任意,但是下面认证函数名必须为authenticate(因为cbv源码里执行流程的认证函数为这个名),不明白原理,可看上一篇文章 def authenticate(self,request): current_token = request._request.GET.get('token') #获取原生request里的token与数据库里的token做判断 obj_token = models.UserToken.objects.filter(token=current_token).first() if not obj_token: raise exceptions.AuthenticationFailed("用户认证失败") #在rest_framework内部会将这两个字段赋值给request,以后供后续操作使用 return (obj_token.user,obj_token) def authenticate_header(self, *args): pass class OrderView(APIView): ''' 订单业务接口 ''' authentication_classes = [Authication,] #引用上面的认证实例进行认证 def get(self,request,*args,**kwargs): print (request.user,request.auth) #打印用户信息 ret = {'code': 10000, 'msg': None} try: ret['data'] = order_dict except Exception as e: pass return JsonResponse(ret) def post(self,request,*args,**kwargs): pass def put(self,request,*args,**kwargs): pass def delete(self,request,*args,**kwargs): pass
这样需要每定义一个函数都要加上
authentication_classes = [Authication,]
urls.py
from django.conf.urls import url from django.contrib import admin from api_test import views urlpatterns = [ url(r'^admin/', admin.site.urls), url(r'^api/v1/auth/$',views.AuthView.as_view()), url(r'^api/v1/order/$',views.OrderView.as_view()), ]
源码流程
用户认证流程是在self.dispatch下的self.initial下 self.perform_authentication(request) 函数进行的,此函数调用的是 rest_framework.request.Request类下的user方法,user方法调用 self._authenticate()方法,下面对其进行分析
_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 if user_auth_tuple is not None: #③ self._authenticator = authenticator self.user, self.auth = user_auth_tuple return
再看 _not_authenticated方法
def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() #没有认证时,返回AnnoymousUser 匿名用户 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() #没有认证时返回None else: self.auth = None
说明
①、这里的self.authenticators里面的值由最开始传入Request类的authenticators参数带入,而authenticators参数的值,最终由APIView类下的authentication_classes代入(self.dispatch ---> self.initialize_request ---> self.get_authenticators ---> self.authentication_classes),如果自己在view函数定义了这个authentication_classes对象,默认会用自己自定义的
②、执行authentication_classes 这里引入的类下面的authenticate方法
A、如果有authenticate 方法抛出异常,执行self._not_authenticated()方法
B、如果没有异常,有返回值,则执行步骤③ ,将步骤②的返回值 user_auth_tuple 赋给 self.user和self.auth(即 request.user和request.auth,可在views里打印)
C、没有异常,没有返回值,则会循环执行authentication_classes下的所有认证方法,直到执行完毕,return则终止本次循环
自定义全局认证类
分析源码发现认证函数定义在APIView下的authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES,查看api_settings源码
rest_framework/settings.py
api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS) def reload_api_settings(*args, **kwargs): setting = kwargs['setting'] if setting == 'REST_FRAMEWORK': #可以在django全局的settings.py里定义这个REST_FRAMEWORK的值,做全局认证,如下: api_settings.reload()
settings.py
import utils #① REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":['utils.auth.Authication'] #② }
此时,在views.py里默认定义的所有方法都会加载这个认证类,如果不想认证,则在函数下直接加上
authentication_classes = []
即可,这样就不需要进行认证了
说明
①、这里要说明的是,认证的类此时不能写在views下面,需要自定义一个路径,所以就在项目同级目录创建了一个 utils目录,并创建认证文件auth.py,将认证的类写到这个文件下
②、这里为什么 REST_FRAMEWORK 为字典,并且键值为DEFAULT_AUTHENTICATION_CLASSES,是由authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES得到
看源码分析
另外
可以自定义认证失败时的匿名用户和token信息为None,如下
def _not_authenticated(self): """ Set authenticator, user & authtoken representing an unauthenticated request. Defaults are None, AnonymousUser & None. """ self._authenticator = None if api_settings.UNAUTHENTICATED_USER: self.user = api_settings.UNAUTHENTICATED_USER() #自定义此处返回的值 else: self.user = None if api_settings.UNAUTHENTICATED_TOKEN: self.auth = api_settings.UNAUTHENTICATED_TOKEN() #自定义此处返回的值 else: self.auth = None
settings.py(一般不关注)
import utils #① REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES":['utils.auth.Authication'] #② "UNAUTHENTICATED_USER":"None", "UNAUTHENTICATED_TOKEN" :"None" }
内部认证
rest_framework内部的认证类
from rest_framework.authentication import BaseAuthentication
查看 BaseAuthentication源码
class BaseAuthentication(object): def authenticate(self, request): #默认如果为定义会抛出异常 raise NotImplementedError(".authenticate() must be overridden.") def authenticate_header(self, request): #返回的http头部信息 pass
所以每次在views里自定义认证类的时候,都要重写 authenticate 函数和authenticate_header函数,并且继承 BaseAuthentication类

浙公网安备 33010602011771号