源碼分析數據庫
dispath方法內 self.initial(request, *args, **kwargs) 進入三大認證django
# 認證組件:校驗用戶(遊客,合法用戶,非法用戶)
# 遊客:表明校驗經過直接進入下一步校驗,
# 合法用戶:表明校驗經過,將用戶存儲在request.user中,在進入下一步校驗
# 非法用戶:表明校驗失敗,拋出異常,返回403權限異常結果
self.perform_authentication(request)
# 權限組件:校驗用戶權限,必須登陸,全部用戶登陸讀寫,遊客只讀,自定義用戶角色
# 認證經過:能夠進入下一步校驗
# 認證失敗:拋出異常,返回403權限異常結果
self.check_permissions(request)
# 頻率組件:限制視圖接口被訪問的頻率次數 限制的條件
# 沒有達到限次:正常訪問接口
# 達到限次:限制時間內不能訪問,限制時間到達後,能夠從新訪問
self.check_throttles(request)
認證組件api
def _authenticate(self): # 遍歷拿到一個個認證器,進行認證 # self.authenticators配置的一堆認證類產生的認證類對象組成的 list for authenticator in self.authenticators: try: # 認證器(對象)調用認證方法authenticate(認證類對象self, request請求對象) # 返回值:登錄的用戶與認證的信息組成的 tuple # 該方法被try包裹,表明該方法會拋異常,拋異常就表明認證失敗 user_auth_tuple = authenticator.authenticate(self) except exceptions.APIException: self._not_authenticated() raise # 返回值的處理 if user_auth_tuple is not None: self._authenticator = authenticator # 如何有返回值,就將 登錄用戶 與 登錄認證 分別保存到 request.user、request.auth self.user, self.auth = user_auth_tuple return # 若是返回值user_auth_tuple爲空,表明認證經過,可是沒有 登錄用戶 與 登錄認證信息,表明遊客 self._not_authenticated()
權限組件緩存
self.check_permissions(request) 認證細則: def check_permissions(self, request): # 遍歷權限對象列表獲得一個個權限對象(權限器),進行權限認證 for permission in self.get_permissions(): # 權限類必定有一個has_permission權限方法,用來作權限認證的 # 參數:權限對象self、請求對象request、視圖類對象 # 返回值:有權限返回True,無權限返回False if not permission.has_permission(request, self): self.permission_denied( request, message=getattr(permission, 'message', None) )
權限六表分析ide
models.py源碼分析
from django.db import models # Create your models here. # 若是自定義User表後,再另外一個項目中採用原生User表,完成數據遷移時,可能失敗 # 1.卸載Django,從新裝 # 2.將Django.contrib下面的admin\auth下的數據庫記錄文件清空 from django.contrib.auth.models import AbstractUser class User(AbstractUser): mobile = models.CharField(max_length=11,unique=True) class Meta: db_table = 'api_user' verbose_name = '用戶表' verbose_name_plural = verbose_name def __str__(self): return self.username
t_model.pypost
# django腳本話啓動 import os, django os.environ.setdefault("DJANGO_SETTINGS_MODULE", "day74.settings") django.setup() from api import models user = models.User.objects.first() # print(user.username) # print(user.groups.first().name) # print(user.user_permissions.first().name) from django.contrib.auth.models import Group group = Group.objects.first() # print(group.name) # print(group.user_set.first().username) # print(group.permissions.first().name) from django.contrib.auth.models import Permission p_16 = Permission.objects.filter(pk=16).first() print(p_16.user_set.first().username) p_17 = Permission.objects.filter(pk=17).first() print(p_17.group_set.first().name)
自定義認證類url
settings.pyspa
# drf配置 REST_FRAMEWORK = { # 認證類配置 'DEFAULT_AUTHENTICATION_CLASSES': [ # 'rest_framework.authentication.SessionAuthentication', # 'rest_framework.authentication.BasicAuthentication', 'api.authentications.MyAuthentication', ], }
authentications.pyrest
from rest_framework.authentication import BaseAuthentication from rest_framework.exceptions import AuthenticationFailed from . import models class MyAuthentication(BaseAuthentication): # 同前臺請求拿認證信息auth,沒有auth是遊客,返回None # 有auth進行校驗 失敗是非法用戶拋異常,成功是合法用戶,返回(用戶,認證信息) def authenticate(self, request): # 前臺在請求頭攜帶信息。默認用Authorization字段攜帶認證信息 # 後臺固定在請求對象的META字段中HTTP_AUTHORIZATION獲取 auth = request.META.get('HTTP_AUTHORIZATION',None) # 處理遊客 if auth is None: return None # 設置認證字段 auth_list = auth.split() # 校驗合法仍是非法 if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'): raise AuthenticationFailed('認證信息有誤,非法用戶') # 合法用戶須要從auth_list[1]中解析出來 if auth_list[1] != 'abc.123.xyz': # 校驗失敗 raise AuthenticationFailed('用戶校驗失敗,非法用戶') user = models.User.objects.filter(username='admin').first() if not user: raise AuthenticationFailed('用戶數據有誤,非法用戶') return (user, None)
系統權限類
1)AllowAny: 認證規則所有返還True:return True 遊客與登錄用戶都有全部權限 2) IsAuthenticated: 認證規則必須有登錄的合法用戶:return bool(request.user and request.user.is_authenticated) 遊客沒有任何權限,登錄用戶纔有權限 3) IsAdminUser: 認證規則必須是後臺管理用戶:return bool(request.user and request.user.is_staff) 遊客沒有任何權限,登錄用戶纔有權限 4) IsAuthenticatedOrReadOnly 認證規則必須是隻讀請求或是合法用戶: return bool( request.method in SAFE_METHODS or request.user and request.user.is_authenticated ) 遊客只讀,合法用戶無限制
urls.py
from django.conf.urls import url from . import views urlpatterns = [ url(r'^test', views.TestAPIView.as_view()),
url(r'^test1', views.TestAuthenticatedAPIView.as_view()),
]
settings.py
REST_FRAMEWORK = { # 權限類配置 'DEFAULT_PERMISSION_CLASSES': [ 'rest_framework.permissions.AllowAny', ], }
api/views.py
from rest_framework.views import APIView from rest_framework.generics import GenericAPIView from rest_framework.viewsets import GenericViewSet,ViewSet from utils.response import APIResponse from rest_framework.permissions import IsAuthenticated class TestAPIView(APIView): def get(self, request, *args, **kwargs): return APIResponse(0, 'test get ok') class TestAuthenticatedAPIView(APIView): permission_classes = [IsAuthenticated] def get(self, request, *args, **kwargs): return APIResponse(0,'test 登陸才能訪問接口 ok')
自定義權限類
建立繼承BasePermission的權限類, 實現has_permission方法, 實現體根據權限規則 肯定有無權限, 進行全局或局部配置
認證規則: 知足設置的用戶條件,表明有權限,返回True, 不知足設置的用戶條件,表明有權限,返回False
urls.py
from django.conf.urls import url from . import views urlpatterns = [ url(r'^test2', views.TestAdminOrReadOnlyAPIView.as_view()), ]
permissions.py
from rest_framework.permissions import BasePermission from django.contrib.auth.models import Group class MyPermission(BasePermission):
# 只讀接口判斷 def has_permission(self, request, view): r1 = request.method in ('GET', 'HEAD', 'OPTIONS')
# group爲當前所屬的全部分組 group = Group.objects.filter(name='管理員').first() groups = request.user.groups.all() r2 = group and groups r3 = group in groups
# 讀接口都權限,寫接口必須指定分組下的登陸用戶 return r1 or (r2 and r3)
views.py
# 遊客只讀,登陸用戶只讀,只有登陸用戶屬於管理員分組,才能夠增刪改
from utils.permissions import MyPermission class TestAdminOrReadOnlyAPIView(APIView): permission_classes = [MyPermission]
# 全部用戶均可以訪問 def get(self, request, *args, **kwargs): return APIResponse(0, '自定義讀 OK')
# 必須是自定義管理員分組下的用戶 def post(self, request, *args, **kwargs): return APIResponse(0, '自定義寫 OK')
自定義頻率類
自定義一個繼承 SimpleRateThrottle 類 的頻率類,設置一個 scope 類屬性,屬性值爲任意見名知意的字符串,在settings配置文件中,配置drf的DEFAULT_THROTTLE_RATES,格式爲 {scope字符串: '次數/時間'},在自定義頻率類中重寫 get_cache_key 方法
settings.py
REST_FRAMEWORK = { # 頻率限制條件配置 'DEFAULT_THROTTLE_RATES': { 'sms':'3/min' }, }
api/url.py
urlpatterns = [ url(r'^sms/$', views.TestSMSAPIView.as_view()), ]
api/throttles.py
from rest_framework.throttling import SimpleRateThrottle class SMSRateThrottle(SimpleRateThrottle): scope = 'sms' # 只對提交手機號的get方法進行限制 def get_cache_key(self, request, view): mobile = request.query_params.get('mobile') # 沒有手機,不作頻率限制 if not mobile: return None # 返回能夠根據手機號變化,且不易重複的字符串,做爲操做緩存的key return 'throttle_%(scope)s_%(ident)s' % {'scope': self.scope, 'ident': mobile}
views.py
class TestSMSAPIView(APIView): # 局部配置頻率校驗 throttle_classes = [SMSRateThrottle] def get(self, request, *args, **kwargs): return APIResponse(0,'get 獲取驗證碼 OK') def post(self, request, *args, **kwargs): return APIResponse(0,'post 獲取驗證碼 OK')