drf 三大認證:
認證:
# 全局配置:
-在全局(認證組件只能決定request.user,不是斷定權限的地方,所以一般配置全局)
REST_FRAMEWORK = {
# 認證組件
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_jwt.authentication.JSONWebTokenAuthentication'
],
}
# 局部禁用(login 模塊):
authentication_classes = ''
permission_classes = ''
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
# authentication_classes = [JSONWebTokenAuthentication]
#自定義認證類:
1) 如果使用session認證,drf默認提供了SessionAuthentication
2) 如果使用drf-jwt認證框架,drf-jwt框架提供了JSONWebTokenAuthentication
3) 如果是自定義簽發與校驗token,才需要將校驗token的算法封裝到自定義的認證類中
from rest_framework_jwt.authentication import JSONWebTokenAuthentication
class MyAuthentication(BaseAuthentication):
def authenticate(self, request):
"""
1) 從請求頭中拿到前台提交的token(一般從HTTP_AUTHORIZATION中拿,也可以與前台約定)
-- 如果設置了反爬等措施,校驗一下反爬(頭 token)
2)沒有token,返回None,代表游客
3)有token,進入校驗
-- 不通過:拋AuthenticationFailed異常,代表非法用戶
-- 通過:返回 (user, token),代表合法用戶
"""
pass
#認證規則:
認證組件:校驗用戶 - 游客、合法用戶、非法用戶
游客:代表校驗通過,直接進入下一步校驗(權限校驗)
合法用戶:代表校驗通過,將用戶存儲在request.user中,再進入下一步校驗(權限校驗)
非法用戶:代表校驗失敗,拋出異常,返回403權限異常結果
只要通過認證不管是游客還是登錄用戶,request.user都有值
權限:
#drf 自帶權限組件:
IsAuthenticated, IsAdminUser, AllowAny, IsAuthenticatedOrReadOnly
#自定義:
自定義權限類:
1) drf默認提供了一些權限類
AllowAny:游客和登錄用戶有全權限
IsAuthenticated:只有登錄用戶有全權限
IsAdminUser:只有后台用戶(admin用戶)有全權限
IsAuthenticatedOrReadOnly:游客有讀權限,登錄用戶有全權限
2)如果有特殊需要,需要自定義權限類
如:只有superuser有權限、只有vip用戶有權限、只有某ip網段用戶有權限、只有某個視圖及其子類有權限
根據需求,request和view的輔助,制定權限規則判斷條件
默認權限:
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
#permissions.py (vip 分組權限管理)
from rest_framework.permissions import BasePermission
class MyAPIView(APIView):
permission_classes = [permissions.VIPUserPermission]
class VIPUserPermission(BasePermission): # 只要vip分組用戶有權限
def has_permission(self, request, view):
for group in request.user.groups.all():
if group.name.lower() == 'vip':
return True # 有權限
return False # 無權限
認證與權限組件綁定使用:
1)每一個視圖類都要進行認證校驗,且認證規則一致,所以全局配置認證類即可
2)每一個視圖類都要進行權限校驗,默認配置的是不限制(AllowAny),但實際開發中,視圖類的訪問權限不盡相同,所以要在具體
的視圖類,配置具體的權限規則
from rest_framework.viewsets import ViewSet
class UserViewSet(ViewSet):
# 權限:只有VIP用戶可以查看個人詳細詳細
permission_classes = [permissions.VIPUserPermission]
def retrieve(self, request, *args, **kwargs):
return APIResponse(results={
'username': request.user.username,
'email': request.user.email,
'mobile': request.user.mobile,
'data_joined': request.user.date_joined,
})
頻率:
from rest_framework.throttling import SimpleRateThrottle
"""
自定義頻率類
1) drf默認提供了一些頻率類
AnonRateThrottle:只對游客進行頻率限制
UserRateThrottle:對所有用戶進行頻率限制
2)如果有特殊需要,需要自定義頻率類
如:對ip進行限次、對電話進行限制、對視圖某些信息進行限次
"""
class MobileRateThrottle(SimpleRateThrottle):
"""
1)設置scope字符串類屬性,同時在settings中進行drf配置DEFAULT_THROTTLE_RATES
eg: DEFAULT_THROTTLE_RATES = {'mobile': '1/min'}
2)重寫get_catch_key方法:
返回與限制條件有關的字符串,表示限制
返回None,表示不限制
"""
scope = 'mobile'
def get_cache_key(self, request, view):
if not request.user.is_authenticated or not request.user.mobile:
return None # 匿名用戶 或 沒有電話號的用戶 都不限制
# 只要有電話號的用戶踩進行限制
return self.cache_format % {
'scope': self.scope,
'ident': request.user.mobile
}
多方式登錄:
from rest_framework.views import APIView
class LoginAPIView(APIView):
""" 重點
1)token只能由 登錄接口 簽發
2)登錄接口也是APIView的子類,使用一定會進行 認證、權限 組件的校驗
結論:不管系統默認、或是全局settings配置的是何認證與權限組件,登錄接口不用參與任何認證與權限的校驗
所以,登錄接口一定要進行 認證與權限 的局部禁用
"""
authentication_classes = []
pagination_class = []
def post(self, request, *args, **kwargs):
serializer = serializers.LoginModelSerializer(data=request.data)
serializer.is_valid(raise_exception=True) # 內部在全局鈎子中完成token的簽發
return APIResponse(results={
'username': serializer.content.get('user').username,
'token': serializer.content.get('token')
})
#serializer.py
from rest_framework_jwt.serializers import jwt_payload_handler, jwt_encode_handler
import re
class LoginModelSerializer(serializers.ModelSerializer):
# post請求,序列化默認當做create動作進行校驗,需要校驗數據庫,create動作username會拋用戶已存在異常
# 拋用戶已存在異常是多余的,所以自定義系統校驗規則即可
username = serializers.CharField(min_length=3, max_length=16)
password = serializers.CharField(min_length=3, max_length=16)
class Meta:
model = models.User
fields = ('username', 'password')
# 用全局鈎子,完成token的簽發
def validate(self, attrs):
# 1)通過 username 和 password 完成多方式登錄校驗,得到user對象
user = self._validate_user(attrs)
# 2)user對象包裝payload載荷
payload = jwt_payload_handler(user)
# 3)payload載荷簽發token
token = jwt_encode_handler(payload)
# 4)將user與token存儲到serializer對象中,方便在視圖類中使用
self.content = {
'user': user,
'token': token
}
return attrs
def _validate_user(self, attrs):
username = attrs.get('username')
password = attrs.get('password')
if re.match(r'.*@.*', username): # 郵箱
user = models.User.objects.filter(email=username).first() # type: models.User
elif re.match(r'^1[3-9][0-9]{9}$', username): # 電話
user = models.User.objects.filter(mobile=username).first()
else: # 用戶名
user = models.User.objects.filter(username=username).first()
if not user or not user.check_password(password):
raise serializers.ValidationError({'message': '用戶信息異常'})
return user
自定義簽發token :
1)將請求數據交給序列化類,執行序列化校驗
2)在序列化全局校驗鈎子中,完成user的認證與token的簽發,保存在序列化對象的content中
3)在視圖類中從序列化對象的content中拿user與token相關信息返回
注:多方式登錄體現在 請求的賬號類型可能是用戶名、郵箱或手機等,采用不同字段校驗數據庫即可