Django之DRF之认证组件
# from rest_framework.views import APIView
# APIView 中的 dispatch 中 执行的 self.initial(request,*args,**kwargs)中的
# APIView---->dispatch------>self.initial------>三个校验
# self.perform_authentication(request) # 认证校验
# self.check_permissions(request) # 权限校验
# self.check_throttles(request) # 频率校验
# 这里的self 指的是我写的那个序列化类
def perform_authentication(self, request):
# 这里的request 是新的request 原生的request 在 新request._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.
"""
# 返回的user 是 新的request 中的一个 被伪装成属性的方法(@property)
# 想要超看这个 需要导包 from rest_frmework.request import Request
request.user
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
这里的user方法属于 :
from rest_framework.request import Request
# Request类中--->user方法----->里面执行了 _authenticate(self) 方法
是 被伪装成数据属性,
@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 是 Request 对象
self._authenticate() # 这里执行的是 Request 类中的_authenticate()
return self._user
重点的逻辑就是如下这段代码:
# APIView-->dispatch--->self.initial--->self.perform_authentication(request)
# --->reuqest.user---->self._authenticate
def _authenticate(self):
"""
Attempt to authenticate the request using each authentication instance
in turn.
尝试依次使用每个身份验证实例对请求进行身份验证。
"""
# 这里的 self.autenticators 是一个元组,里面存的是一个个实例化的对象
# 这里元组 是 Request 类在实例化的时候执行 __init__方法的时候赋值来的
for authenticator in self.authenticators:
try:
# 这里是执行自己写的认证类中的的 authenticate()方法,得到的是一个元组(这里面存的是 user对象 和 一个auth对象)!!!如果有返回值的话
user_auth_tuple = authenticator.authenticate(self)
# 如果该方法中返回 user对象 和 auth对象
except exceptions.APIException:
self._not_authenticated() # 如果没有通过认证走这个方法
raise
# 如果user_auth_tuple 有值,不为空
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
认证组件的使用
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=32)
price = models.DecimalField(max_digits=8, decimal_places=2)
publish_time = models.DateTimeField(auto_now_add=True) # 自动添加创建时间
authors = models.ManyToManyField('Author')
publish = models.ForeignKey('Publish') # 一对多
def test(self):
return self.title+'>>'+str(self.price)
class Author(models.Model):
name = models.CharField(max_length=32)
age = models.IntegerField()
authordetail = models.OneToOneField('AuthorDetail')
class AuthorDetail(models.Model):
tel_num = models.BigIntegerField()
addr = models.CharField(max_length=32)
class Publish(models.Model):
name = models.CharField(max_length=32)
addr = models.CharField(max_length=32)
email = models.EmailField()
# 用户类
class User(models.Model):
name = models.CharField(max_length=32)
pwd = models.CharField(max_length=32)
# token类 与用户类一对一关系
class Token(models.Model):
user = models.OneToOneField(to='User')
token = models.CharField(max_length=64)
认证类的建立(一般新建一个py文件)
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.authentication import BaseAuthentication
from app01 import models
class MyAuth(BaseAuthentication):
# 在认证组件中 需要重写 authenticate方法来 完成 用户认证逻辑
def authenticate(self, request):
# request值得是个对象
token = request.GET.get('token')
token_obj = models.Token.objects.filter(token=token).first()
if token_obj:
# 有值表示登录了
return
else:
# 没有值,则报错
raise AuthenticationFailed('您没有登录')
views.py
from django.shortcuts import render
from django.http.response import JsonResponse
# Create your views here.
from rest_framework.views import APIView
from rest_framework.response import Response
from app01 import models
from app01.myser import BookSerializer
from app01 import auths
from django.core.exceptions import ObjectDoesNotExist
import uuid
class Book(APIView):
# 这里固定 需要些 这个认证类的列表,列表中存的是 一个一个认证的类
authentication_classes = [auths.MyAuth,]
# get 获取所有书籍信息
def get(self, request, id):
response = {'status': 100, 'msg': '成功'}
print(id)
if not id:
book_list = models.Book.objects.all()
# 第一个参数是要序列化的queryset对象,如果要序列化多条,必须制定many=True
# 当instance形参被传入的实参是单个参数的时候,many=False
book_serializer = BookSerializer(book_list, many=True)
else:
print(id)
book_obj = models.Book.objects.filter(pk=id).first()
book_serializer = BookSerializer(book_obj, many=False)
print(book_serializer.data)
response['books'] = book_serializer.data
return Response(response)
def post(self, request, id):
response = {'status': 100, 'msg': '成功'}
# 提交的字典
book = request.data
# 传统方法,创建对象保存
print(book)
# 新方法,通过序列化组件保存,必须继承自ModelSerializer
book_ser = BookSerializer(data=book)
# is_valid 提交的字段校验通过
if book_ser.is_valid():
book_ser.save()
response['book'] = book_ser.data
else:
response['msg'] = book_ser.errors # errors 是序列化类 中的钩子函数 raise来的报错信息
return Response(response)
def put(self, request, id):
response = {'status': 100, 'msg': '修改成功'}
if id:
# 提交的字典
book = request.data
# 传统方法,创建对象保存
print(book)
book_obj = models.Book.objects.filter(pk=id).first()
# 新方法,通过序列化组件保存,必须继承自ModelSerializer
book_ser = BookSerializer(data=book, instance=book_obj)
# is_valid 提交的字段校验通过
if book_ser.is_valid():
# 这里save()做修改
book_ser.save()
response['book'] = book_ser.data
else:
response['msg'] = book_ser.errors
else:
response['msg'] = '修改对象不存在'
return Response(response)
def delete(self, request, id):
models.Book.objects.filter(pk=id).delete()
response = {'status': 100, 'msg': '删除成功'}
return Response(response)
class Login(APIView):
# 局部禁用 认证
authentication_classes = []
def post(self, request):
response = {'code': 100, 'msg': '登录成功'}
name = request.data.get('name')
pwd = request.data.get('pwd')
print(name, pwd)
try:
# get()方法,获取 有且只有一条的 才不报错,其他情况都抛异常
ret = models.User.objects.filter(name=name, pwd=pwd).get()
# 登录成功后要去token 表中去存数据
# 表里有 数据或没有数据
# 1. 先生成随机字符串 用uuid
token = uuid.uuid4()
# 2. 存入token表
# update_or_create() 方法 先查后改,查到就修改,没查到就新增 根据 user 去查
models.Token.objects.update_or_create(user=ret, defaults={'token': token})
response['token'] = token
except ObjectDoesNotExist as exc:
response['code'] = 101
response['msg'] = '用户名或密码错误'
except Exception as e:
response['code'] = 102
response['msg'] = str(e)
return Response(response)
不存token的认证
def get_token(id,salt='123'):
import hashlib
md=hashlib.md5()
md.update(bytes(str(id),encoding='utf-8'))
md.update(bytes(salt,encoding='utf-8'))
return md.hexdigest()+'|'+str(id)
def check_token(token,salt='123'):
ll=token.split('|')
import hashlib
md=hashlib.md5()
md.update(bytes(ll[-1],encoding='utf-8'))
md.update(bytes(salt,encoding='utf-8'))
if ll[0]==md.hexdigest():
return True
else:
return False
class TokenAuth():
def authenticate(self, request):
token = request.GET.get('token')
success=check_token(token)
if success:
return
else:
raise AuthenticationFailed('认证失败')
def authenticate_header(self,request):
pass
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_token(user.pk)
# 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)
from rest_framework.authentication import BaseAuthentication
class TokenAuth():
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
class Course(APIView):
authentication_classes = [TokenAuth, ]
def get(self, request):
return HttpResponse('get')
def post(self, request):
return HttpResponse('post')
总结:
-
局部使用: 只要在 需要认证的类里面 固定写入你所需的哪些认证(认证类)
# 这里固定 需要些 这个认证类的列表,列表中存的是 一个一个认证的类 authentication_classes = [auths.MyAuth,]
-
全局使用: 只要在settings文件中配置 REST_FRAMEWORK 即可
# settings.py 中配置 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auths.MyAuth", ] }
-
局部禁用:只要在不需要使用全局使用的认证的 视图类中清除authentication_classes中你不需要的认证即可
authentication_classes=[]
阅读源码体会:
- 如果在settings.py 文件中配置了 REST_FRAMEWORK,先默认从项目settings中取
- 如果取不到,才去默认的drf配置文件中取
- 用户如果在 视图类中配置了authentication_classes, 则优先从用户配置的取