django-rest-framework 基礎三 認證、許可權和頻率
1. 認證
登入介面: 登入成功只要給前端返回json
格式字串,這個字串中帶一個隨機字串(可以使用uuid生成)
登入介面步驟:
前端傳入使用者名稱和密碼,然後去user表中查詢,能找到說明使用者和密碼沒問題,登入成功,然後在userToken表中存一條記錄,說明登入過了,再返回前端一個json字串
1.1 登入介面
models.py
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=64)
password = models.CharField(max_length=128)
user_type = models.IntegerField(choices=((1, "超級管理員"),(2,"管理員"),(3,"普通使用者")))
class UserToken(models.Model):
user = models.OneToOneField(to=User,on_delete=models.CASCADE)
userToken = models.CharField(max_length=64)
views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.viewsets import GenericViewSet
from authenticated.models import User,UserToken
from rest_framework.response import Response
from rest_framework.decorators import action
import uuid
class UserView(GenericViewSet):
@action(methods=['POST'], detail=False)
def login(self,request):
username = request.data.get("username")
password = request.data.get("password")
user = User.objects.filter(username=username, password=password).first()
if not user:
return Response({"code":1001,"msg":"使用者名稱或密碼錯誤"})
token = str(uuid.uuid4()) # 獲取一個不重複的值,做唯一標識
# userToken表中有就更新,沒有就建立
#UserToken.objects.update_or_create(user=user, defaults={'token': token})
UserToken.objects.update_or_create(user=user, defaults={'userToken': token})
# 返回資訊,並帶著token
return Response({"code":1000,"msg":"登入成功",'userToken': token})
urls.py
from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers
router = routers.SimpleRouter()
router.register('user',views.UserView, "user")
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls))
]
訪問:
post請求: http://127.0.0.1:8000/user/login/
1.2 認證
有了登入介面,就可以實現認證,如果要呼叫別的介面必須要先登入才可以。
例如有個圖書的表有五個介面,要訪問圖書的五個介面必須就登入。
步驟:
1. 先寫一個類,繼承BaseAuthentication,並重寫authenticate方法,在方法中校驗是否登入,登入則返回兩個值,沒有則拋異常
from rest_framework.authentication import BaseAuthentication
class xxxx(BaseAuthentication):
def authenticate(self, request):
2. 全域性配置和區域性配置
全域性配置:
settings.py中
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":["authenticated.authentication.LoingAuth"]
}
區域性配置:
在檢視類中:
authentication_classes = [xxxx,]
禁止區域性署配置:
authentication_classes = []
登入則返回兩個值:
request.user 當前登入的使用者
request.auth 為當前登入使用者的token
示例:
models.py
from django.db import models
# Create your models here.
class User(models.Model):
username = models.CharField(max_length=64)
password = models.CharField(max_length=128)
user_type = models.IntegerField(choices=((1, "超級管理員"),(2,"管理員"),(3,"普通使用者")))
class UserToken(models.Model):
user = models.OneToOneField(to=User,on_delete=models.CASCADE)
userToken = models.CharField(max_length=64)
class Book(models.Model):
name = models.CharField(max_length=64)
price = models.DecimalField(max_digits=5, decimal_places=2)
author = models.CharField(max_length=64)
認證功能,在app中新建authentication.py
from rest_framework.authentication import BaseAuthentication
from authenticated.models import UserToken
from rest_framework.exceptions import AuthenticationFailed
class LoingAuth(BaseAuthentication):
def authenticate(self, request):
token = request.query_params.get('token')
user_token = UserToken.objects.filter(userToken=token).first()
if not user_token:
raise AuthenticationFailed("請先登入")
return user_token.user, token
views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.viewsets import GenericViewSet
from authenticated.models import User,UserToken
from rest_framework.response import Response
from rest_framework.decorators import action
import uuid
from rest_framework.viewsets import ModelViewSet
from authenticated.models import Book
from authenticated.serializer import BookSerializer
from authenticated.authentication import LoingAuth
class UserView(GenericViewSet):
@action(methods=['POST'], detail=False)
def login(self,request):
username = request.data.get("username")
password = request.data.get("password")
user = User.objects.filter(username=username, password=password).first()
if not user:
return Response({"code":1001,"msg":"使用者名稱或密碼錯誤"})
token = str(uuid.uuid4())
# userToken表中有就更新,沒有就建立
#UserToken.objects.update_or_create(user=user, defaults={'token': token})
UserToken.objects.update_or_create(user=user, defaults={'userToken': token})
return Response({"code":1000,"msg":"登入成功",'token': token})
class BookView(ModelViewSet):
# 只對BookView單獨進行認證(區域性配置)
authentication_classes = [LoingAuth,]
queryset = Book.objects.all()
serializer_class = BookSerializer
路由urls.py
from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers
router = routers.SimpleRouter()
router.register('user',views.UserView, "user")
router.register('books', views.BookView,"books")
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls))
]
全域性配置settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES":["authenticated.authentication.LoingAuth"],
}
# authenticated.authentication.LoingAuth 為寫的認證的類
# 全域性配置後區域性配置的就可以取消了。
# 但是全域性配置後如果有些類不想讓它有認證,比如登入介面,它不能有認證否則就死迴圈了。
# 全域性配置後,單獨取消某一個介面的認證:
class UserView(ViewSet):
authentication_classes = [] # 讓它等於空就可以了。
2. 許可權
所有的介面必須登入後才能訪問(給每個檢視加認證),登入成功後如果是普通使用者則只可檢視全部或單條資料。如果想要增刪改必須是管理員或超級管理員。
演示可以把五個介面寫成兩個檢視:
在應用目錄下建立permission.py
檔案
permission.py
from rest_framework.permissions import BasePermission
class userPermission(BasePermission):
def has_permission(self, request, view):
user_type = request.user.user_type
# (1, "超級管理員"),(2,"管理員"),(3,"普通使用者"),如果小於3說明是管理或超管使用者
if user_type <3:
return True
else:
return False
檢視views.py
from rest_framework.viewsets import GenericViewSet
from authenticated.models import User, UserToken
from authenticated.models import Book
from authenticated.serializer import BookSerializer
from authenticated.authentication import LoingAuth
from rest_framework.mixins import CreateModelMixin, ListModelMixin, DestroyModelMixin, UpdateModelMixin, \
RetrieveModelMixin
#使用者登入介面此處省略(見上面1.2認證)
# 檢視全部和單條。只要登入了誰都可以訪問
class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
authentication_classes = [LoingAuth, ]
queryset = Book.objects.all()
serializer_class = BookSerializer
# 只有管理和超管使用者可以 建立、修改、新增
from authenticated.permission import userPermission
class BookDetailView(GenericViewSet, CreateModelMixin, UpdateModelMixin, DestroyModelMixin):
authentication_classes = [LoingAuth]
permission_classes = [userPermission]
queryset = Book.objects.all()
serializer_class = BookSerializer
路由 urls.py
:
from django.contrib import admin
from django.urls import path,include
from authenticated import views
from rest_framework import routers
router = routers.SimpleRouter()
router.register('user',views.UserView, "user")
router.register('books', views.BookView,"books")
router.register('bookdetail', views.BookDetailView,"bookdetail")
urlpatterns = [
path('admin/', admin.site.urls),
path('', include(router.urls))
]
全域性配置settings.py
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES":["authenticated.permission.userPermission"],
}
# authenticated.permission.userPermission 為寫的許可權的類
# 全域性配置後區域性配置的就可以取消了。
# 全域性配置後,單獨取消某一個介面的許可權:
class UserView(ViewSet):
permission_classes = [] # 讓它等於空就可以了。
普通使用者訪問的時候會報沒有許可權
2.1 許可權總結:
兩個檢視:
BookView:獲取所有,獲取單條
BookDetailView:刪除,修改,新增
上面兩個檢視都需要登入:authentication_classes = [LoginAuth, ]
BookDetailView必須有許可權才能,加了一個許可權,permission_classes = [UserPermission, ]
編寫許可權步驟
第一步:寫一個類,繼承BasePermission,重寫has_permission,判斷如果有許可權,返回True,如果沒有許可權,返回False
第二步:區域性配置和全域性配置
區域性配置
class BookDetailView(GenericViewSet, CreateModelMixin, DestroyModelMixin, UpdateModelMixin):
permission_classes = [UserPermission, ]
全域性配置
settings.py
REST_FRAMEWORK={
"DEFAULT_PERMISSION_CLASSES":["authenticated.permission.userPermission",]
}
3. 頻率
限制訪問的頻率
在應用目錄建立throttle.py
檔案用來限制頻率
throttle.py
from rest_framework.throttling import SimpleRateThrottle
class IpThrottle(SimpleRateThrottle):
scope = 'min_3' # 在settings.py定義給哪個類限制的頻率
# get_cache_key 返回什麼就以什麼做限制,現在是以IP做限制
def get_cache_key(self, request, view):
return request.META.get('REMOTE_ADDR') # 返回的是客戶端的IP,以IP做限制
# return request.user.id # 返回已經登入的使用者的id,以使用者id做限制
views.py
from authenticated.throttle import IpThrottle
# 檢視全部和單條。只要登入了誰都可以訪問
class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
authentication_classes = [LoingAuth, ]
throttle_classes = [IpThrottle] # 訪問BookView類做限制
queryset = Book.objects.all()
serializer_class = BookSerializer
settings.py
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES":{
'min_3':'3/m',
},
}
# min_3 就是上面throttle.py.IpThrottle裡scope定義的,這個一定要和scope定義的一致
# 3/m 每一分鐘訪問3次 ('s', 'sec', 'm', 'min', 'h', 'hour', 'd', 'day')
# 如果throttle.py.IpThrottle裡還有別的限制的類,如果scope也是為min_3,那它也是每分鐘訪問3次的限制
超過3次就會報錯:
上面的配置為區域性配置,也可以設定全域性配置
settings.py
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES" : ["authenticated.throttle.IpThrottle"], # 全域性配置
"DEFAULT_THROTTLE_RATES":{
'min_3':'3/m',
},
}
# 同樣設定了全域性,區域性的throttle_classes = [IpThrottle]就可以不寫了
# 如果只是某個類禁用:
throttle_classes = []
3.1 頻率總結
步驟:
第一步:寫一個類,繼承SimpleRateThrottle,重寫類屬性:scope,和get_cache_key方法
get_cache_key返回什麼,就以什麼做限制,
scope配置檔案中要用
第二步:在配置檔案中配置
settings.py中
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_RATES":{
'min_3':'3/m', # minute_3是scope的字串,一分鐘訪問3次
},
}
第三步: 使用
1. 區域性使用--》檢視類中
class BookView(GenericViewSet, ListModelMixin, RetrieveModelMixin):
throttle_classes = [IPThrottle]
2. 全域性使用--配置檔案中
settings.py中
REST_FRAMEWORK = {
"DEFAULT_THROTTLE_CLASSES" : ["authenticated.throttle.IpThrottle"],
"DEFAULT_THROTTLE_RATES":{
'min_3':'3/m', # minute_3是scope的字串,一分鐘訪問3次
},
}