认证组件
简单实例
settings.py
INSTALLED_APPS = [ 'rest_framework', ]
urls.py
urlpatterns = [ path('admin/', admin.site.urls), path('api/v1/auth/',views.AuthView.as_view()), ]
models.py
from django.db import models class UserInfo(models.Model): USER_TYPE = ( (1,'普通用户'), (2,'VIP'), (3,'SVIP') ) user_type = models.IntegerField(choices=USER_TYPE) username = models.CharField(max_length=32) password = models.CharField(max_length=64) class UserToken(models.Model): user = models.OneToOneField(UserInfo,on_delete=models.CASCADE) 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 API import models
from rest_framework.request import Request
from rest_framework import exceptions
from rest_framework.authentication import BasicAuthentication
ORDER_DICT = {
1:{
'name':'apple',
'price':15
},
2:{
'name':'dog',
'price':100
}
}
def md5(user):
import hashlib
import time
#当前时间,相当于生成一个随机的字符串
ctime = str(time.time())
m = hashlib.md5(bytes(user,encoding='utf-8'))
m.update(bytes(ctime,encoding='utf-8'))
return m.hexdigest()
class AuthView(object):
'''用于用户登录验证'''
def post(self,request,*args,**kwargs):
ret = {'code':1000,'msg':None}
try:
user = request._request.POST.get('username')
pwd = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=user,password=pwd).first()
if not obj:
ret['code'] = 1001
ret['msg'] = '用户名或密码错误'
#为用户创建token
token = md5(user)
#存在就更新,不存在就创建
models.UserToken.objects.update_or_create(user=obj,defaults={'token':token})
ret['token'] = token
except Exception as e:
ret['code'] = 1002
ret['msg'] = '请求异常'
return JsonResponse(ret)
class Authentication(APIView):
'''认证'''
def authenticate(self,request):
token = request._request.GET.get('token')
token_obj = models.UserToken.objects.filter(token=token).first()
if not token_obj:
raise exceptions.AuthenticationFailed('用户认证失败')
#在rest framework内部会将这两个字段赋值给request,以供后续操作使用
return (token_obj.user,token_obj)
def authenticate_header(self, request):
pass
class OrderView(APIView):
'''订单相关业务'''
authentication_classes = [Authentication,] #添加认证
def get(self,request,*args,**kwargs):
#request.user
#request.auth
ret = {'code':1000,'msg':None,'data':None}
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
总结:
-写一个认证类(继承BaseAuthentication) -重写authenticate方法,把request对象传入 -能从request对象中取出用户携带的token,根据token判断是否登录过 -如果登录过,返回两个值 user对象,token对象 -如果没有登录过抛异常 -全局使用 -在settings中配置 REST_FRAMEWORK={ "DEFAULT_AUTHENTICATION_CLASSES":["app01.service.auth.Authentication",] } -局部使用 -在视图类中配置: authentication_classes=[Authentication,] -局部禁用: -在视图类中配置: authentication_classes=[]
认证源码分析
-APIView中的dispatch ---> self.initial(认证,频率,权限) ---> self.perform_authentication(认证) ---> 本质又调用了新的request对象的user方法
--> request.user内部执行了:self._authenticate(注意self是新的request对象) ---> 循环拿到一个个认证类的对象,执行对象的authenticate方法,传入request对象
---> 一个个认证类的对象是在reuqest对象实例化的时候传入的 ---> APIView中的get_authenticators,通过列表推导式生成一个个的认证类对象,然后传入request对象中