认证源码分析

#1、APIAPIView #进来
#2、APIAPIView类中有很多方法,找到dispatch方法 ,请求来了肯定要走dispatch
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs) #request已经变了
self.initial(request, *args, **kwargs) #这个request是包装后的
#3、来到initial方法、权限、频率等都在该方法中
def initial(self, request, *args, **kwargs): #包装后的request
self.perform_authentication(request) #认证
self.check_permissions(request)
self.check_throttles(request)
#4、来到perform_authentication,
def perform_authentication(self, request):
request.user #此时就要去包装后的request中找user
#----------------------------------上边都是APIAPIView类中的方法
#----------------------------------下边看都是Request类中的方法
#5、点from rest_framework.request import Request进来,找到user方法
@property
def user(self):
self._authenticate() #执行_authenticate方法
#6、来到_authenticate
def _authenticate(self):
for authenticator in self.authenticators: #循环取值,authenticators是什么
user_auth_tuple = authenticator.authenticate(self)
#7、它是实例化的时候传过来的,那么实例化是在什么时候完成的?是在第2步request包装的时候完成的,实例化的时候,此时来到第二步,找到request包装的过程
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
self.authenticators = authenticators or ()
#----------------------------又回到了APIAPIView中
#8、此时来到第二步,找到request包装的过程,点进去,注意此时来到了APIAPIView中
def initialize_request(self, request, *args, **kwargs):
return Request(
return Request(
request,
parsers=self.get_parsers(), #解析的
authenticators=self.get_authenticators(), #现在重点关注这,这里的self是谁?是APIAPIView,其实是我们写的类,我们继承了APIAPIView
negotiator=self.get_content_negotiator(), #分页
parser_context=parser_context #解析
#9、自己写的类中没有get_authenticators方法,来它的父类APIAPIView找
def get_authenticators(self):
return [auth() for auth in self.authentication_classes] #直接点进去
#10、同样是APIAPIView中的authentication_classes
class APIView(View):
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
#11、先不管api_settings是什么,如果我们自己类中写一个authentication_classes,
#如下 注意:来到了view.py
class Auth():
pass
class Login(APIView):
authentication_classes=[Auth,] #那么第9步会从这里拿到Auth,并加括号实例化产生对象
def get(self,request,*args,**kwargs):
ret = models.Book.objects.all()
#12、往回看,第8步
authenticators=self.get_authenticators()
#authenticators其实就是列表中包含一个一个对象
#[Auth对象,,,]
#--------------------------回到 Request看
#13、看5,6步,先看第5步
for authenticator in self.authenticators:
#把[Auth对象,,,]列表中的对象一个一个取出来
user_auth_tuple = authenticator.authenticate(self)
#调用authenticator 的 authenticate方法,也就是Auth对象的方法,
#所以在自己的Auth方法中把pass去掉,写authenticate方法,,
#注意注意,这里的authenticate(self)带有self参数,正常情况下,authenticator对象,调用自己的方法,不需要传参,这里传了,这里的self。是谁?是Request对象,所以,我们在写Auth中的authenticate时也需要传参
class Auth():
def authenticate(self,request):
源码分析

def _authenticate(self):
for authenticator in self.authenticators: #已经知道是一个一个对象
#执行authenticate(自己写的)
user_auth_tuple = authenticator.authenticate(self)
if user_auth_tuple is not None: #
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple #
return
自己手写验证

class UserInfo(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
# 用户token
class UserToken(models.Model):
token = models.CharField(max_length=64)
user = models.OneToOneField(to=UserInfo)

from django.shortcuts import render
from rest_framework.views import APIView
from app01 import models
from django.core.exceptions import ObjectDoesNotExist
import hashlib
import time
from django.http import JsonResponse
from app01 import MySerializer
# Create your views here.
def get_token(name): #写一个生成token的方法
# 生成一个md5对象
md5 = hashlib.md5()
# 往里添加值,必须是bytes格式
# time.time()生成时间戳类型,转成字符串,再encode转成bytes格式
md5.update(str(time.time()).encode('utf-8'))
md5.update(name.encode('utf-8'))
return md5.hexdigest()
# 登录
class Login(APIView):
def post(self, request, *args, **kwargs):
response = {'status': 100, 'msg': '登录成功'}
name = request.data.get('name')
pwd = request.data.get('pwd')
try:
user = models.UserInfo.objects.get(name=name, pwd=pwd)
# 校验通过,登录成功,生成一个随机字符串(身份标识)token
token = get_token(name)
# 保存到数据库
# update_or_create更新或者创建,因为不可能同一用户访问10次,生成10次
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'] = 102
# response['msg']='未知错误'
response['msg'] = str(e)
return JsonResponse(response, safe=False)

class Books(APIView):
def get(self, request, *args, **kwargs):
response = {'status': 100, 'msg': '查询成功'}
# 必须登录以后,才能获取数据
# 取出token,取数据库验证,是否登录
token = request.query_params.get('token')
ret = models.UserToken.objects.filter(token=token)
if ret:
# 认证通过,是登录用户
ret = models.Book.objects.all()
book_ser = MySerializer.BookSerializer(ret, many=True)
response['data'] = book_ser.data
else:
response['status'] = 101
response['msg'] = '认证不通过'
return JsonResponse(response, safe=False)
使用auth
1、认证类(在新建的py文件中)

rom rest_framework.authentication import BaseAuthentication
class TokenAuth(BaseAuthentication): #尽量继承,避免抛出异常
def authenticate(self, request):
token = request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if token_obj:
return
else:
raise AuthenticationFailed('认证失败')
def authenticate_header(self,request):
pass
2、view层

def get_random(name):
import hashlib
import time
md=hashlib.md5()
md.update(bytes(str(time.time()),encoding='utf-8'))
md.update(bytes(name,encoding='utf-8'))
return md.hexdigest()
class Login(APIView):
def post(self,reuquest):
back_msg={'status':1001,'msg':None}
try:
name=reuquest.data.get('name')
pwd=reuquest.data.get('pwd')
user=models.User.objects.filter(username=name,password=pwd).first()
if user:
token=get_random(name)
models.UserToken.objects.update_or_create(user=user,defaults={'token':token})
back_msg['status']='1000'
back_msg['msg']='登录成功'
back_msg['token']=token
else:
back_msg['msg'] = '用户名或密码错误'
except Exception as e:
back_msg['msg']=str(e)
return Response(back_msg)
class Course(APIView):
authentication_classes = [TokenAuth, ]
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
总结
#局部使用,只需要在视图类里加入: authentication_classes = [TokenAuth, ]
全局使用
#全局使用
#在setting中配置:
REST_FRAMEWORK={
'DEFAULT_AUTHENTICATION_CLASSES':['app01.MyAuth.LoginAuth',]
}
app01.MyAuth.LoginAuth认证的地址
一般情况下,认证不会写在view中,都是单独写在一个py文件中,
#局部禁用,(不可能别人还没有登陆,就需要认证)
在需要认证的类中,
authentication_classes = []

#authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
在读源码的时候,有这么一句,当时的处理是自己写一个authentication_classes
#如果步自己写的话,就会用api_settings中的,
#一直点进来得到,
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
不存数据库版的token
#存数据库耗资源,并且需要IO,耗费时间

def create_token(user_id):
md5 = hashlib.md5()
md5.update(user_id.encode('utf-8'))
md5.update(settings.password.encode('utf-8')) #加密盐,在settings中写的字符串
hex = md5.hexdigest()
token = hex + '|' + user_id
print(token)
return token
# 登录
class Login(APIView):
authentication_classes = [] #局部禁用auth认证,还没登陆,不能验证
def post(self, request, *args, **kwargs): #发送post请求,
response = {'status': 100, 'msg': '登录成功'}
name = request.data.get('name')
pwd = request.data.get('pwd')
try:
user = models.UserInfo.objects.get(name=name, pwd=pwd)
user_info_json = json.dumps({'name': user.name, 'id': user.pk})
# 生产dafgasdewf|{'id':user.pk}的token
token = create_token(str(user.pk))
response['token'] = token
except ObjectDoesNotExist as e:
response['status'] = 101
response['msg'] = '用户名或密码错误'
except Exception as e:
response['status'] = 102
# response['msg']='未知错误'
response['msg'] = str(e)
return JsonResponse(response, safe=False)
自己写auth认证

#先看class LoginAuth(BaseAuthentication):
#理念就是,把token取出来,切分后,按照原来的加密凡是加密,判断是否相同
def check_token(token):
ret = True #两个变量
user_info=None
try:
ll = token.split('|')
# "eef48b787e24381258aa71d0d53615c2,{\"id\": 1}"
md5 = hashlib.md5() #切分后,把|后边的按照原来的加密方式,进行加密,判断是否与|前边的相同
md5.update(ll[1].encode('utf-8'))
md5.update(settings.password.encode('utf-8')) #同样需要加密盐,更安全
hex = md5.hexdigest()
if not hex == ll[0]:
ret = False
else:
user_info=ll[1]
except Exception as e:
ret = False
return ret,user_info
class LoginAuth(BaseAuthentication):
# 函数名一定要叫authenticate,接收必须两个参数,第二个参数是request对象
def authenticate(self, request):
# 从request对象中取出token(也可以从其它地方取)
token = request.query_params.get('token')
ret, user_info = check_token(token) #拿到token,调用check_token
if ret:
return user_info, None
# 如果查不到,抛异常
raise exceptions.APIException('您认证失败')
来源:https://www.cnblogs.com/pdun/p/11246810.html
