Django之DRF之认证组件python
# 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方法属于 :git
from rest_framework.request import Request # Request类中--->user方法----->里面执行了 _authenticate(self) 方法
是 被假装成数据属性,django
@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
重点的逻辑就是以下这段代码:app
# 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()
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)
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.pyide
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)
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 便可post
# settings.py 中配置 REST_FRAMEWORK = { "DEFAULT_AUTHENTICATION_CLASSES": ["app01.auths.MyAuth", ] }
局部禁用:只要在不须要使用全局使用的认证的 视图类中清除authentication_classes中你不须要的认证便可ui
authentication_classes=[]