models.py:
在穆棱等地區(qū),都構(gòu)建了全面的區(qū)域性戰(zhàn)略布局,加強(qiáng)發(fā)展的系統(tǒng)性、市場前瞻性、產(chǎn)品創(chuàng)新能力,以專注、極致的服務(wù)理念,為客戶提供做網(wǎng)站、網(wǎng)站建設(shè) 網(wǎng)站設(shè)計制作按需定制,公司網(wǎng)站建設(shè),企業(yè)網(wǎng)站建設(shè),高端網(wǎng)站設(shè)計,全網(wǎng)整合營銷推廣,成都外貿(mào)網(wǎng)站制作,穆棱網(wǎng)站建設(shè)費(fèi)用合理。
class User(models.Model):
username = models.CharField(max_length=32)
password = models.CharField(max_length=32)
class UserToken(models.Model):
user = models.OneToOneField(to='User', on_delete=models.CASCADE)
token = models.CharField(max_length=64)
views.py:
from rest_framework.response import Response
from rest_framework.viewsets import ModelViewSet
import uuid
class BookViewSet(ModelViewSet):
queryset = Book.objects
serializer_class = BookModelSerializer
@action(methods=['GET', 'POST'], detail=False)
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user = models.User.objects.filter(username=username, password=password).first()
if user:
token = str(uuid.uuid4())
models.UserToken.objects.update_or_create(user=user, defaults={'token': token})
return Response({'code': 100, 'msg': '登錄成功', 'token': token})
return Response({'code': 101, 'msg': '用戶名或密碼錯誤'})
urls.py:
from rest_framework.routers import DefaultRouter, SimpleRouter
router = DefaultRouter()
router.register('books4', views.BookViewSet)
urlpatterns = [
path('admin/', admin.site.urls),
]
urlpatterns += router.urls
drf
認(rèn)證、權(quán)限、頻率三大功能是在哪里實(shí)現(xiàn)的?在 APIView
源碼中實(shí)現(xiàn),所有繼承 APIView
的類都可以實(shí)現(xiàn)三大認(rèn)證
APIView
的類的 as_view
返回 view
函數(shù)的內(nèi)存地址,view
運(yùn)行后調(diào)用了 APIView
中的 dispatch
方法,三大認(rèn)證在 self.initial(request, *args, **kwargs)
函數(shù)中調(diào)用
def dispatch(self, request, *args, **kwargs):
request = self.initialize_request(request, *args, **kwargs)
try:
self.initial(request, *args, **kwargs)
except Exception as exc:
response = self.handle_exception(exc)
APIView
--> initial
def initial(self, request, *args, **kwargs):
self.perform_authentication(request) # 認(rèn)證
self.check_permissions(request) # 權(quán)限
self.check_throttles(request) # 頻率
APIView
-->perform_authentication(request)
源碼中只返回了登錄用戶或者匿名用戶對象
該方法調(diào)用了 request.user
,也就是重寫后的 request 屬性中的 user
,我們知道那里的 user
有被調(diào)用觸發(fā)的方法 和 被賦值觸發(fā)的方法
def perform_authentication(self, request):
"""
`request.user` or `request.auth` is accessed.
"""
request.user
轉(zhuǎn)到重寫 request
對象的Request
類中:
Request
-->user(property)
可以看到調(diào)用的是 Request
類中的 _authenticate()
@property
def user(self):
if not hasattr(self, '_user'):
with wrap_attributeerrors():
self._authenticate()
return self._user
Request
-->_authenticate
核心代碼
該方法循環(huán) Request
類中的 authenticators
列表(認(rèn)證類列表)
調(diào)用認(rèn)證類 authenticator
中的 authenticate(self)
方法,并放回元組user_auth_tuple
,包含登錄用戶對象(user)和 auth
如果 user_auth_tuple
不為 None,則 request
對象便獲取了user
對象和 auth
對象
authenticate(self)
方法,并放回 user
對象和 auth
對象 def _authenticate(self):
for authenticator in self.authenticators:
try:
user_auth_tuple = authenticator.authenticate(self) # 調(diào)用類中的authenticate方法
except exceptions.APIException:
self._not_authenticated()
raise
if user_auth_tuple is not None:
self._authenticator = authenticator
self.user, self.auth = user_auth_tuple
return
self._not_authenticated()
authenticators 怎么來的?
可以看出,authenticators
是 Request
類實(shí)例化時傳進(jìn)來的參數(shù),那么我們就得回到APIView
類中的 initialize_request(self, request, *args, **kwargs)
方法,因?yàn)槭窃谀抢飳?shí)例化了 Request
類
class Request:
def __init__(self, request, parsers=None, authenticators=None,
negotiator=None, parser_context=None):
self.authenticators = authenticators or ()
APIView
-->initialize_request
可以看到調(diào)用了自己的 get_authenticators()
方法
def initialize_request(self, request, *args, **kwargs):
return Request(
request,
parsers=self.get_parsers(),
authenticators=self.get_authenticators(),
negotiator=self.get_content_negotiator(),
parser_context=parser_context
)
APIView
-->get_authenticators()
這是個列表生成式,從視圖函數(shù)類中的認(rèn)證類列表 authentication_classes
中取出一個個認(rèn)證類加括號實(shí)例化為對象,并存在類表中放回,那么get_authenticators()
方法放回的是一個認(rèn)證類對象的列表
def get_authenticators(self):
"""
Instantiates and returns the list of authenticators that this view can use.
"""
return [auth() for auth in self.authentication_classes]
繞來繞去,最終明白,我們可以在視圖類中定義 authentication_classes
列表,存放認(rèn)證類,在觸發(fā)視圖函數(shù)類時就會執(zhí)行列表中認(rèn)證類重寫的 authenticate(self)
方法,并放回登錄的用戶對象和auth
APIView
-->check_permissions(request)
get_permissions()
是獲取認(rèn)證類對象列表
has_permission(request, self)
是我們自定義權(quán)限類時要改寫的方法,返回True或False
def check_permissions(self, request):
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request,
message=getattr(permission, 'message', None),
code=getattr(permission, 'code', None)
)
APIView
-->check_throttles(request)
def check_throttles(self, request):
throttle_durations = []
for throttle in self.get_throttles():
if not throttle.allow_request(request, self):
throttle_durations.append(throttle.wait())
if throttle_durations:
durations = [
duration for duration in throttle_durations
if duration is not None
]
duration = max(durations, default=None)
self.throttled(request, duration)
class APIView(View):
# The following policies may be set at either globally, or per-view.
renderer_classes = api_settings.DEFAULT_RENDERER_CLASSES
parser_classes = api_settings.DEFAULT_PARSER_CLASSES
authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES # 認(rèn)證類列表
throttle_classes = api_settings.DEFAULT_THROTTLE_CLASSES # 頻率限制類
permission_classes = api_settings.DEFAULT_PERMISSION_CLASSES # 權(quán)限類
content_negotiation_class = api_settings.DEFAULT_CONTENT_NEGOTIATION_CLASS
metadata_class = api_settings.DEFAULT_METADATA_CLASS
versioning_class = api_settings.DEFAULT_VERSIONING_CLASS
三個認(rèn)證類在 drf 配置文件中的配置
DEFAULTS = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.BasicAuthentication'
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.AllowAny',
],
'DEFAULT_THROTTLE_CLASSES': [],
}
校驗(yàn)過登錄用戶數(shù)據(jù)之后把token信息存到usertoken表中
class LoginViewSet(ModelViewSet):
queryset = models.User.objects
serializer_class = UserSerializer
@action(methods=['POST'], detail=False) # api 要多寫一個 login/
def login(self, request):
username = request.data.get('username')
password = request.data.get('password')
user_obj = models.User.objects.filter(username=username, password=password).first()
if not user_obj:
return Response({'code': 1001, 'msg': '該用戶不存在'})
uuid_str = str(uuid.uuid4())
# 過濾條件中user=user的對象,而不是pk
models.UserToken.objects.update_or_create(user=user_obj, defaults={'token': uuid_str}) # 別漏寫default后的字典
return Response({'code': 1000, 'msg': '登錄成功', 'token': uuid_str})
1、新建一個認(rèn)證模塊,寫一個認(rèn)證類繼承 BaseAuthentication
(多態(tài)),重寫authenticate
方法,在方法中校驗(yàn)是否登錄,是則返回兩個值(request.user
和 request.auth
)
from rest_framework.authentication import BaseAuthentication
# 登錄認(rèn)證
class LoginAuth(BaseAuthentication):
def authenticate(self, request):
token = request.data.get('token')
is_login = models.UserToken.objects.filter(token=token).first()
if not is_login:
raise AuthenticationFailed('您沒有登錄')
return is_login.user, token
2、在視圖類中書寫 authentication_classes
,存放導(dǎo)入進(jìn)來的認(rèn)證類
from app01.authentications import LoginAuth, ChangePermission
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = []
3、全局配置(在項(xiàng)目文件夾下的 settings.py 文件中配置)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'app01.authentications.LoginAuth',
],
}
1、在認(rèn)證模塊中,寫一個權(quán)限類繼承 BasePermission
(多態(tài)),重寫has_permission
方法,在方法中校驗(yàn)該用戶的用戶類型,是否有權(quán)限執(zhí)行該視圖類,返回 True
或者 False
, 還可以自定義報錯信息 self.message
from rest_framework.permissions import BasePermission
class ChangePermission(BasePermission):
def has_permission(self, request, view):
self.message = '您是%s,沒有修改權(quán)限' % request.user.get_user_type_display() # 根據(jù)源碼,可以修改權(quán)限的提示信息
user_type = request.user.user_type
if user_type != 1:
return False
return True
2、在視圖類中書寫 permission_classes
,存放導(dǎo)入進(jìn)來的權(quán)限類
注意:在定義權(quán)限認(rèn)證類前需要先定義登錄認(rèn)證類,否則沒有用戶對象 requset.user
來做權(quán)限認(rèn)證
from app01.authentications import LoginAuth, ChangePermission
class BookViewChange(ViewSetMixin, CreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
permission_classes = [ChangePermission, ]
class BookViewChange(ViewSetMixin, CreateAPIView, RetrieveUpdateDestroyAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
permission_classes = []
3、全局配置(在項(xiàng)目文件夾下的 settings.py 文件中配置)
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'app01.authentications.ChangePermission',
],
}
1、在認(rèn)證模塊中,寫一個頻率限制類繼承 SimpleRateThrottle
(多態(tài)),重寫類屬性 scope
和 get_cache_key
方法,該方法返回什么,就以什么為限制,scope配置文件中要用
from rest_framework.throttling import SimpleRateThrottle
# 頻率限制
class IPThrottling(SimpleRateThrottle):
scope = 'minute_3'
# 返回什么就以什么做限制
def get_cache_key(self, request, view):
# return request.META.get('REMOTE_ADDR') # 客戶端ip地址
return request.user.id # 用戶id
2、與其它兩個認(rèn)證不同,他需要在項(xiàng)目配置文件中配置:
REST_FRAMEWORK = [
'DEFAULT_THROTTLE_RATES': {
'minute_3': '3/m' # minute_3是scope的字符串,一分鐘訪問3次
'minute_5':'5/m'
}
]
2、在視圖類中書寫 throttle_classes
,存放導(dǎo)入進(jìn)來的權(quán)限類
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
throttle_classes = [IPThrottling, ]
class BookViewSet(ViewSetMixin, ListAPIView):
queryset = models.Book.objects
serializer_class = BookModelSerializer
authentication_classes = [LoginAuth, ]
throttle_classes = []
3、全局配置(在項(xiàng)目文件夾下的 settings.py 文件中配置)
REST_FRAMEWORK = {
'DEFAULT_THROTTLE_CLASSES': [
'app01.authentications.IPThrottling'
],
'DEFAULT_THROTTLE_RATES': {
'minute_3': '3/m', # minute_3是scope的字符串,一分鐘訪問3次
# 'minute_5': '5/m'
}
}