【DRF-04】rest-framework之认证
- 1.认证基本使用
- 1.1:问题:有些API(订单信息)需要用户登录成功之后,才能访问;有些无需登录就能访问。
- 1.2:解决思路:用户登录后,生成token--保存在数据库中,前端带token,允许访问,不带token,不允许访问。
- 1.3:models
from django.db import models
class UserInfo(models.Model):
user_type_choices = (
(1,'普通用户'),
(2,'VIP'),
(3,'SVIP'),
)
user_type = models.IntegerField(choices=user_type_choices)
username = models.CharField(max_length=32,unique=True)
password = models.CharField(max_length=64)
class UserToken(models.Model):
user = models.OneToOneField(to='UserInfo',on_delete=models.CASCADE)
token = models.CharField(max_length=64)
- 1.4:用户登录并且生成token,自定义认证类
from app01 import models
from rest_framework import exceptions
from django.http import JsonResponse
from rest_framework.authentication import BasicAuthentication
from django.shortcuts import HttpResponse
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(APIView):
"""
用于用户登录认证
"""
def post(self,request,*args,**kwargs):
self.dispatch()
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 Authtication(BasicAuthentication):
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):
# 模拟数据库订单数据
ORDER_DICT = {
1:{"name":"电脑","price":12340,"pcs":2},
2:{"name":"手机","price":1999,"pcs":1}
}
authentication_classes = [Authtication,] # 局部使用
def get(self,request,*args,**kwargs):
# request.user #authenticate 返回的第一个元素
# request.auth #authenticate 返回的第二个元素
ret = {'code':1000,'msg':None,'data':None}
try:
ret['data'] = self.ORDER_DICT
except Exception as e:
pass
#son_dumps_params={"ensure_ascii":False} 解决使用JsonResponse返回显示中文
return JsonResponse(ret,json_dumps_params={"ensure_ascii":False})
-
1.5:效果展示
-
2.内置认证类,一般我们自定义的认证类需要继承:BaseAuthentication
-
3.全局使用
- 3.1:上述1中是将自定义认证类,局部使用。
- 3.2:全局使用,注意:认证类不能放到视图中,需要单独放置
- 3.3:setting
REST_FRAMEWORK = {
# 全局使用的认证类
"DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication','app01.utils.auth.Authtication', ],
# "DEFAULT_AUTHENTICATION_CLASSES":['app01.utils.auth.FirstAuthtication', ],
# "UNAUTHENTICATED_USER":lambda :"匿名用户"
"UNAUTHENTICATED_USER":None, # 匿名,request.user = None
"UNAUTHENTICATED_TOKEN":None,# 匿名,request.auth = None
}
- 3.4:utils/auth.py
from rest_framework import exceptions
from app01 import models
from rest_framework.authentication import BaseAuthentication
class FirstAuthtication(BaseAuthentication):
def authenticate(self,request):
pass
def authenticate_header(self, request):
pass
class Authtication(BaseAuthentication):
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):
return 'Basic realm="api"'
- 3.5:如果某个API不需要使用认证,重写authentication_classes 置为空即可
class AuthView(APIView):
authentication_classes = [ ]
def post(self,request,*args,**kwargs):
pass
- 4.源码流程(重点)
- 4.1:首先请求进来,走APIView的dispath,对request进行了封装initialize_request;
def dispatch(self, request, *args, **kwargs):
"""
`.dispatch()` is pretty much the same as Django's regular dispatch,
but with extra hooks for startup, finalize, and exception handling.
"""
self.args = args
self.kwargs = kwargs
# 对原生的request进行加工(丰富了一些功能)
# Request(request,parsers=self.get_parsers(),authenticators=self.get_authenticators(),
# negotiator=self.get_content_negotiator(),parser_context=parser_context)
# request(原生的request,MyAuthentication对象)
# 获取原生request:request._request
# 获取认证类的对象:request.BasicAuthentication
# 1.封装request
request = self.initialize_request(request, *args, **kwargs)
self.request = request
self.headers = self.default_response_headers # deprecate?
try:
# 2.认证
self.initial(request, *args, **kwargs)
# Get the appropriate handler method
if request.method.lower() in self.http_method_names:
handler = getattr(self, request.method.lower(),
self.http_method_not_allowed)
else:
handler = self.http_method_not_allowed
response = handler(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
self.response = self.finalize_response(request, response, *args, **kwargs)
return self.response
- 4.2:initialize_request方法
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(), # [Foo(),Bar()]
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
- 4.3:get_authenticators方法,authentication_classes如果自己有就取自己的,没有就取配置文件
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
# self.authentication_classes = [Foo,Bar]
return [auth() for auth in self.authentication_classes]
class APIView(View):
# The following policies may be set at either globally, or per-view.
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES
pass
- 4.4:initial方法,如果正常就反射执行对应的get/post方法,如果错误,抛出异常。
def initial(self, request, *args, **kwargs):
"""
Runs anything that needs to occur prior to calling the method handler.
"""
self.format_kwarg = self.get_format_suffix(**kwargs)
# Perform content negotiation and store the accepted info on the request
neg = self.perform_content_negotiation(request)
request.accepted_renderer, request.accepted_media_type = neg
# Determine the API version, if versioning is in use.
version, scheme = self.determine_version(request, *args, **kwargs)
request.version, request.versioning_scheme = version, scheme
# Ensure that the incoming request is permitted
# 4.实现认证,权限,频率
self.perform_authentication(request)
self.check_permissions(request)
self.check_throttles(request)
- 4.5:perform_authentication方法
def perform_authentication(self, request):
"""
Perform authentication on the incoming request.
Note that if you override this and simply 'pass', then authentication
will instead be performed lazily, the first time either
`request.user` or `request.auth` is accessed.
"""
request.user
- 4.6:执行request中的user方法
@property
def user(self):
"""
Returns the user associated with the current request, as authenticated
by the authentication classes provided to the request.
"""
if not hasattr(self, '_user'):
with wrap_attributeerrors():
# 获取认证对象,进行一步一步的认证
self._authenticate()
return self._user
- 4.6:执行_authenticate方法
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
"""
# [BasicAuthentication对象,]
for authenticator in self.authenticators:
try:
# 执行认证类的authenticate方法
# 1.如果authenticate方法抛出异常,self._not_authenticated()执行
# 2.如果authenticate方法正常,如果有返回值,必须是元祖。self.user, self.auth = user_auth_tuple
# 3.没报错,也没返回值,返回None。当前这个认证不处理,交给下一个认证处理
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
self._not_authenticated()
- 4.7:循环执行authenticate方法,即我们自定义的认证类