源碼分析
"""
1)APIView的dispath(self, request, *args, **kwargs)
2)dispath方法內 self.initial(request, *args, **kwargs) 進入三大認證
# 認證組件:校驗用戶 - 游客、合法用戶、非法用戶
# 游客:代表校驗通過,直接進入下一步校驗(權限校驗)
# 合法用戶:代表校驗通過,將用戶存儲在request.user中,再進入下一步校驗(權限校驗)
# 非法用戶:代表校驗失敗,拋出異常,返回403權限異常結果
self.perform_authentication(request)
# 權限組件:校驗用戶權限 - 必須登錄、所有用戶、登錄讀寫游客只讀、自定義用戶角色
# 認證通過:可以進入下一步校驗(頻率認證)
# 認證失敗:拋出異常,返回403權限異常結果
self.check_permissions(request)
# 頻率組件:限制視圖接口被訪問的頻率次數 - 限制的條件(IP、id、唯一鍵)、頻率周期時間(s、m、h)、頻率的次數(3/s)
# 沒有達到限次:正常訪問接口
# 達到限次:限制時間內不能訪問,限制時間達到后,可以重新訪問
self.check_throttles(request)
3) 認證組件
Request類的 方法屬性 user 的get方法 => self._authenticate() 完成認證
認證的細則:
# 做認證
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()
4) 權限組件
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)
)
"""
自定義認證類
"""
1) 創建繼承BaseAuthentication的認證類
2) 實現authenticate方法
3) 實現體根據認證規則 確定游客、非法用戶、合法用戶
4) 進行全局或局部配置
認證規則
i.沒有認證信息返回None(游客)
ii.有認證信息認證失敗拋異常(非法用戶)
iii.有認證信息認證成功返回用戶與認證信息元組(合法用戶)
"""
utils/authentications.py
# 自定義認證類
# 1)繼承BaseAuthentication類
# 2)重新authenticate(self, request)方法,自定義認證規則
# 3)認證規則基於的條件:
# 沒有認證信息返回None(游客)
# 有認證信息認證失敗拋異常(非法用戶)
# 有認證信息認證成功返回用戶與認證信息元組(合法用戶)
# 4)完全視圖類的全局(settings文件中)或局部(確切的視圖類)配置
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 認證字符串"
auth_list = auth.split()
# 校驗合法還是非法用戶
if not (len(auth_list) == 2 and auth_list[0].lower() == 'auth'):
raise AuthenticationFailed('認證信息有誤,非法用戶')
# 合法的用戶還需要從auth_list[1]中解析出來
# 注:假設一種情況,信息為abc.123.xyz,就可以解析出admin用戶;實際開發,該邏輯一定是校驗用戶的正常邏輯
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
)
游客只讀,合法用戶無限制
"""
# api/views.py
from rest_framework.permissions import IsAuthenticated
class TestAuthenticatedAPIView(APIView):
permission_classes = [IsAuthenticated]
def get(self, request, *args, **kwargs):
return APIResponse(0, 'test 登錄才能訪問的接口 ok')
# 因為默認全局配置的權限類是AllowAny
# settings.py
REST_FRAMEWORK = {
# 權限類配置
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
}
自定義權限類
"""
1) 創建繼承BasePermission的權限類
2) 實現has_permission方法
3) 實現體根據權限規則 確定有無權限
4) 進行全局或局部配置
認證規則
i.滿足設置的用戶條件,代表有權限,返回True
ii.不滿足設置的用戶條件,代表有權限,返回False
"""
# utils/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為當前用戶所屬的所有分組
groups = request.user.groups.all()
r2 = group and groups
r3 = group in groups
# 讀接口大家都有權限,寫接口必須為指定分組下的登陸用戶
return r1 or (r2 and r3)
# 游客只讀,登錄用戶只讀,只有登錄用戶屬於 管理員 分組,才可以增刪改
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')