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=[]