3 restframework-认证
3.1APIView 认证:
- 认证是否已经登陆,如果已经登陆返回元组,如果没有登陆报错
- 源码流程:
执行dispatch方法:
def dispatch(self, request, *args, **kwargs):
#1.封装request 对原生request进行加工,执行initialize_request方法
request = self.initialize_request(request, *args, **kwargs)
try:
#2.认证
self.initial(request, *args, **kwargs)
#--------------------initialize_request-------------------
#执行initialize_request:
def initialize_request(self, request, *args, **kwargs):
"""
Returns the initial request object.
"""
parser_context = self.get_parser_context(request)
#返回实例化Request,把原有的request进行封装,丰富功能
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
#---------------------------------------------------------
#--------------------Request------------------------------
#而Request类中:实例化方法中定义:
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
assert isinstance(request, HttpRequest), (
'The `request` argument must be an instance of '
'`django.http.HttpRequest`, not `{}.{}`.'
.format(request.__class__.__module__, request.__class__.__name__)
)
self._request = request#1
self.parsers = parsers or ()
self.authenticators = authenticators or ()#
self.negotiator = negotiator or self._default_negotiator()
self.parser_context = parser_context
self._data = Empty
self._files = Empty
self._full_data = Empty
self._content_type = Empty
self._stream = Empty
#1 : 获取原生request 通过 request._request
#2 : 获取认证类的对象 通过 request.authenticators
#返回dispatch方法中继续走
#---------------------------------------------------------
#--------------------initial----------------------------
#再initial执行方法:
def initial(self, request, *args, **kwargs):
...
#4.实现认证
self.perform_authentication(request)
#---------------------------------------------------------
#--------------------perform_authentication---------------
#返回当前登陆用户,此时request为封装加工后的request
def perform_authentication(self, request):
request.user
#---------------------------------------------------------
#---------------------------Request----------------------
#执行request为Request类中实例化的对象,通过.user执行user方法
class Request:
...
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
#获取认证对象,进行一步步认证
self._authenticate()
return self._user
#---------------------------------------------------------
#----------------_authenticate-------------------------
def _authenticate(self):
#authentication_classes=[BasicAuthentication对象,]
#authenticators为定义authentication_classes=中的对象
#循环所有认证对象。
for authenticator in self.authenticators:
try:
#通过authenticate方法验证是否登陆,如果登陆返回元组,如果未登陆报错
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
#---------------------------------------------------------
#-------------------------authenticate-----------------------------
def authenticate(self, request):
return (self.force_user, self.force_token)
- 那么如何自己定义认证:
import json
from django.http import HttpResponse
from django.shortcuts import render
from rest_framework.decorators import authentication_classes#用于自定义实现认证
from rest_framework.views import APIView
from rest_framework import exceptions#用于认证失败引发错误
class MyAuthentication(object):
#重写authenticate方法
def authenticate(self,request):
token = request._request.GET.get('token')
print(token)
if not token:
raise exceptions.AuthenticationFailed("用户认证失败")
return ("hello",None)
def authenticate_header(self,val):pass
class DogView(APIView):
#自定义认证类
authentication_classes = [MyAuthentication,]
def get(self,request,*args,**kwargs):
ret = {
"code":1000,
'msg':"OK"
}
return HttpResponse(json.dumps(ret),status=200)
def post(self,request,*args,**kwargs):
return HttpResponse("创建Dog")
#www.xxx.com/?token=123
效果:
- 当访问URL为
<http://127.0.0.1:8000/dog/
- 当访问URL为
<http://127.0.0.1:8000/dog/?token=123>
- 当访问URL为
基于认证总结:
源码流程 dispatch开始
3.2 用户登陆
models.py
class UserInfo(models.Model):
user_type_choice = {
(1,"普通用户"),
(2,"VIP"),
(3,"SVIP")
} user_type = models.IntegerField(choices=user_type_choice)
username = models.CharField(max_length=32,unique=True)
password = models.CharField(max_length=32) class UserToken(models.Model):
user = models.OneToOneField(to="UserInfo")
token = models.CharField(max_length=32)
views.py
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):
ret = {"code":1000,"msg":None}
try:
username = request._request.POST.get('username')
password = request._request.POST.get('password')
obj = models.UserInfo.objects.filter(username=username,password=password).first()
if not obj:
ret["code"] = 1001
ret["msg"] = "用户密码错误"
#为登陆用户创建token
token = md5(username)
#存在更新,不存在创建,保存Token
models.UserToken.objects.update_or_create(user=obj,defaults={"token":token})
except Exception as e:
ret["code"] = 1002
ret["msg"] = "请求异常"
return JsonResponse(ret)
3.3用户登陆实现token认证
#充当数据库数据
ORDER_DICT = {
1: {
'name': '媳妇',
'age': 18,
'gender': '男',
'content': '...'
},
2: {
'name': '老狗',
'age': 19,
'gender': '男',
'content': '....'
}
}
#自定义认证类
class Authtication(object):
def authenticate(self,request):
token = request._request.GET.get("token")
token_obj = models.UserToken.objects.filter(token=token).first()
#如果token表示用户认证失败
if not token_obj:
raise exceptions.AuthenticationFailed("用户认证失败")
#在restframework内部会将这两个字段赋值给request,以供后续使用
#第一个值通过request.user可以获取
#第二个值通过reuqest.auth可以获取
return (token_obj.user,token_obj)
def authenticate_header(self, val): pass
class OrderView(APIView):
authentication_classes = [Authtication,]
def get(self,request,*args,**kwargs):
# token = request._request.GET.get("token")
# if not token:
# return HttpResponse("用户未登陆")
ret = {"code": 1000, "msg": None, "data": None}
try:
ret['data'] = ORDER_DICT
except Exception as e:
pass
return JsonResponse(ret)
#通过restframework让视图函数只做数据处理,认证交给authentication_classes,代码逻辑清晰不少。
3.4认证流程原理
drf认证组件的实现过程:
as_view() --->view --->执行dispatch方法,
为自己dispatch方法。通过initialize_request将request对象进行了重新封装。通过initial执行perform_authentication,Request执行user方法去执行_authenticate,遍历循环authentication_classes所有对象。执行认证类的方法.
有三种情况:
1.如果有返回值,是一个元组 request.user,request.auth
2.如果返回None,下一个认证来处理:
当都返回None:request.user = AnonymousUser, request.auth = None
3.抛出异常,执行self.not_authenticated()当都返回None, request.user = AnonymousUser, request.auth = None.(设置默认值,表示未登陆)
源码中 全局使用配置项:
将自己定义认证类配置在rest_framework配置项目中
#当前项目新建文件夹,新建py文件,放入认证类
settings.py配置
REST_FRAMEWORK = {
#全局使用认证类。
'DEFAULT_AUTHENTICATION_CLASSES':['api.utils.auth.Authtication',],
#设置匿名用户None。
'UNAUTHENTICATED_USER':None,#request.user = None
'UNAUTHENTICATED_TOKEN':None,#当前用户未登陆表示匿名用户 request.auth = None
} #这样类视图都不用加authentication_classes,也可以执行认证。对于不需要认证的类视图,可以添加未 authentication_classes = []
3.5 django内置认证类
- 如下:5个类:
class BaseAuthentication:
class BasicAuthentication(BaseAuthentication):#通过浏览器生成登陆模态框,实现base64加密,把加密后信息放在请求头发过去。
class SessionAuthentication(BaseAuthentication):
class TokenAuthentication(BaseAuthentication):
class RemoteUserAuthentication(BaseAuthentication):
- 为了让我们写认证类,更加规范,自定义认证类都必须要继承于
BaseAuthentication
class BaseAuthentication:
"""
All authentication classes should extend BaseAuthentication.
"""
def authenticate(self, request):
"""
Authenticate the request and return a two-tuple of (user, token).
"""
raise NotImplementedError(".authenticate() must be overridden.")
def authenticate_header(self, request):
#当认证失败时候,浏览器给返回响应头
"""
Return a string to be used as the value of the `WWW-Authenticate`
header in a `401 Unauthenticated` response, or `None` if the
authentication scheme should return `403 Permission Denied` responses.
"""
pass
#所有定义认证类必须有authenticate,authenticate_header方法。否则无法正常执行代码