drf 認證,許可權
阿新 • • 發佈:2020-12-29
1 drf認證功能介紹
- 認證,頻率,許可權
- 使用者是否登入到系統中
- 後期基本上會用JWT的認證
- 自定製的認證
2 認證功能原始碼分析
2.1:drf 認證流程
1 APIView--->dispatch--->self.initial(request, *args, **kwargs)-->self.perform_authentication(request)--->Request.user--->self._authenticate(self):Request類的方法--->self.authenticators:Request類的屬性--->在Request物件例項化的時候傳入的---->Request在什麼時候例項化的?dispatch的時候--->APIView:self.get_authenticators()-->return [auth() for auth in self.authentication_classes]---->如果在自己定義的檢視類中寫了authentication_classes=[類1,類2]---->Request的self.authenticators就變成了我們配置的一個個類的物件
2.2:drf認證核心程式碼
#self._authenticate(self):Request類的方法 def _authenticate(self): for authenticator in self.authenticators: # BookView中配置的一個個類的物件 try: user_auth_tuple = authenticator.authenticate(self) 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
2.3:區域性配置認證類
只要在檢視類中配置authentication_classes = [MyAuthen.LoginAuth, ],就會執行上面的方法,執行認證
3 自定義認證類(重點)
3.1:如何自定義認證類
-定義一個類,繼承BaseAuthentication class LoginAuth(BaseAuthentication): def authenticate(self, request): token = request.GET.get('token') res = models.UserToken.objects.filter(token=token).first() if res: return 元組 else: raise AuthenticationFailed('您沒有登入')
3.2:全域性,區域性使用認證,區域性禁用
3.2.1區域性使用
在檢視類中配置(只要配置了,就是登入以後才能訪問,沒配置,不用登入就能訪問)
class Test(APIView):
authentication_classes = [MyAuthen.LoginAuth, ]
pass
3.2.2:全域性使用
全域性使用(所有介面,都需要登入才能訪問)
settings.py
REST_FRAMEWORK = {
"DEFAULT_AUTHENTICATION_CLASSES": ["app01.MyAuthen.LoginAuth", ]
}
3.2.3:區域性禁用
在想禁用的檢視類上,如下操作
class Test(APIView):
authentication_classes = []
pass
3.3:注意
- 認證類,認證通過可以返回一個元組,有兩個值,第一個值會給,request.user,第二個值會個request.auth
- 認證類可以配置多個,按照從前向後的順序執行,如果前面有返回值,認證就不再繼續往下走了
4:示例
models.py
from django.db import models
# Create your models here.
class UserInfo(models.Model):
name = models.CharField(max_length=255,help_text='使用者名稱')
password = models.CharField(max_length=255,help_text='密碼')
mobile = models.CharField(max_length=255,help_text='手機')
email = models.CharField(max_length=255,help_text='郵箱')
token = models.CharField(max_length=255,null=True)
user_type = models.IntegerField(choices=((1,'vip'),(3,'generic')),default=1)
class Book(models.Model):
title = models.CharField(max_length=255,help_text='書名')
price = models.DecimalField(max_digits=5, decimal_places=2,help_text='價格')
publish = models.ForeignKey(to="Publish", on_delete=models.SET_NULL, null=True, db_constraint=False,help_text='出版社')
@property
def publish_name(self):
return {'id':self.publish_id,'name':self.publish.name,'addr':self.publish.addr}
class Publish(models.Model):
name = models.CharField(max_length=255,help_text='出版社名稱')
addr = models.CharField(max_length=255,help_text='出版社地址')
views.py
from django.shortcuts import render
# Create your views here.
from rest_framework.generics import GenericAPIView
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework.mixins import CreateModelMixin,\
ListModelMixin, UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin
from . import models
from . import serializer
from .util import LearnBaseAuthentication
from .util import LearnBasePermission
from rest_framework.viewsets import GenericViewSet
class Login(APIView):
def post(self, request, *args, **kwargs):
username = request.data.get('username')
password = request.data.get('password')
user_obj = models.UserInfo.objects.filter(name=username, password=password)
if user_obj:
import uuid
token = uuid.uuid4()
models.UserInfo.objects.update_or_create(name=username, defaults={'token': token})
return Response({'code': 200, "token": token, "msg": '登入成功'})
return Response({'code': 100, "msg": '使用者名稱或則密碼錯誤'})
class Book(GenericViewSet, ListModelMixin, CreateModelMixin, UpdateModelMixin,DestroyModelMixin,RetrieveModelMixin):
authentication_classes = [LearnBaseAuthentication, ]
permission_classes = [LearnBasePermission, ]
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
def get(self, request, *args, **kwargs):
return self.list(request, *args, **kwargs)
def get_one(self,request,*args,**kwargs):
return self.retrieve(request,*args,**kwargs)
def post(self, request, *args, **kwargs):
return self.create(request, *args, **kwargs)
def put(self, request, *args, **kwargs):
return self.update(request, *args, **kwargs)
def delete(self, request, *args, **kwargs):
return self.destroy(request, *args, **kwargs)
util.py
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from . import models
class LearnBaseAuthentication(BaseAuthentication):
def authenticate(self, request):
token = request.data.get('token')
print(token, 11)
user = models.UserInfo.objects.filter(token=token)
if user:
return user.last().name, token
else:
raise AuthenticationFailed('使用者名稱或者密碼錯誤')
# class LearnBaseAuthentication(BaseAuthentication):
# def authenticate(self, request):
# token = request.data.get('token')
# try:
# user = models.UserInfo.objects.get(token=token)
# if user:
# return user.username, token
# except Exception:
# raise AuthenticationFailed('使用者名稱或者密碼錯誤!')
class LearnBasePermission(BasePermission):
message = '您沒有許可權'
def has_permission(self, request, view):
user_type = models.UserInfo.objects.get(name=request.user).user_type
if user_type == 1:
return True
else:
return False
serializer
from rest_framework import serializers
from . import models
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['id', 'title', 'price', 'publish_name', 'publish']
extra_kwargs = {
'id': {'required': False},
'publish_name': {'read_only': True},
'publish': {'write_only': True},
}
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = '__all__'
extra_kwargs = {
'id': {'required': False}
}
urls.py
from django.urls import path
from learn import views
urlpatterns = [
path('books/', views.Book.as_view()),
path('login/', views.Login.as_view()),
]
5 自定義許可權功能(重點)
5.1:為什麼要有許可權限制
登入成功以後,超級使用者可以幹某些事,普通使用者不能幹---》超級使用者可以檢視某些介面,普通使用者不能檢視
5.2:自定義許可權
使用寫一個類繼承BasePermission,重寫has_permission
class SuperPermission(BasePermission):
message='許可權不夠'
def has_permission(self, request, view):
# Return `True` if permission is granted, `False` otherwise.
# 超級使用者可以訪問,除了超級使用者以外,都不能訪問
if request.user.user_type == '1':
return True
else:
return False
5.3:許可權(區域性使用,區域性禁用,全域性使用)
5.3.1:區域性使用
class Test():
permission_classes = [MyAuthen.SuperPermission]
def get(self,request,*args,**kwargs):
pass
5.3.2:區域性禁用
class Test():
permission_classes = []
def get(self,request,*args,**kwargs):
pass
5.3.3:全域性使用
settings.py
REST_FRAMEWORK = {
"DEFAULT_PERMISSION_CLASSES": ["....SuperPermission", ]
}
6 許可權原始碼分析
核心程式碼
def check_permissions(self, request):
"""
Check if the request should be permitted.
Raises an appropriate exception if the request is not permitted.
"""
for permission in self.get_permissions():
if not permission.has_permission(request, self):
self.permission_denied(
request, message=getattr(permission, 'message', None)
)
預設配置檔案
7:內建的許可權和認證類
# 內建認證類
from rest_framework.exceptions import AuthenticationFailed
# 內建許可權類
from rest_framework.permissions import BasePermission
8:示例2
需求
寫一個圖書的5個接口出版社的5個介面和登入介面
1:使用者必須登入才能訪問圖書的5個介面
2:必須超級使用者登入後才能訪問出版社5個介面
models.py
from django.db import models
# Create your models here.
class Book(models.Model):
title = models.CharField(max_length=255)
price = models.DecimalField(max_digits=5, decimal_places=2)
publish = models.ForeignKey(to='Publish', on_delete=models.SET_NULL, null=True, db_constraint=False)
class Publish(models.Model):
name = models.CharField(max_length=255)
addr = models.CharField(max_length=255)
class UserInfo(models.Model):
username = models.CharField(max_length=255)
password = models.CharField(max_length=255)
mobile = models.CharField(max_length=25)
email = models.EmailField()
token = models.CharField(max_length=255,null=True)
user_type = models.IntegerField(choices=((1, 'vip'), (2, 'svip'), (3, 'generic')))
urls.py
from django.urls import path
from work import views
urlpatterns = [
path('login/', views.Login.as_view()),
path('books/', views.Book.as_view({'get': "list", 'post': 'create'})),
path('book/<int:pk>/', views.Book.as_view({'get': "retrieve", 'delete': 'destroy', 'put': 'update'})),
path('publishes/', views.Publish.as_view({'get': "list", 'post': 'create'})),
path('publish/<int:pk>/', views.Publish.as_view({'get': "retrieve", 'delete': 'destroy', 'put': 'update'}))
]
serializer.py
from rest_framework import serializers
from . import models
class BookModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Book
fields = ['id', 'title', 'price', 'publish']
class PublishModelSerializer(serializers.ModelSerializer):
class Meta:
model = models.Publish
fields = ['id', 'name', 'addr']
util.py
from . import models
from rest_framework.authentication import BaseAuthentication
from rest_framework.exceptions import AuthenticationFailed
from rest_framework.permissions import BasePermission
# token認證
class TokenBaseAuthentication(BaseAuthentication):
def authenticate(self, request):
visit_path = request.get_full_path()
token = request.data.get('token')
if not token:
raise AuthenticationFailed('先登入後訪問')
print(token)
try:
user_obj = models.UserInfo.objects.get(token=token)
return user_obj, token
except Exception:
raise AuthenticationFailed('使用者名稱或者者密碼錯誤')
# 許可權管理
class TokenBasePermission(BasePermission):
message = '許可權不夠'
def has_permission(self, request, view):
user_obj = request.user
visit = view.__class__.__name__.lower()
print(visit)
if user_obj.user_type == 1 and visit == 'book':
return True
elif user_obj.user_type == 2 and visit == 'publish':
return True
views.py
from django.shortcuts import render
# Create your views here.
import uuid
from rest_framework.viewsets import ModelViewSet
from rest_framework.views import APIView
from rest_framework.response import Response
from . import serializer
from . import models
from . import util
class Login(APIView):
def post(self, request, *args, **kwargs):
msg = {'code': 200, 'msg': None}
username = request.data.get('username')
password = request.data.get('password')
user_obj = models.UserInfo.objects.filter(username=username, password=password)
if not user_obj:
msg['code'] = 100
msg['msg'] = '使用者名稱或者密碼錯誤!'
return Response(msg)
token = uuid.uuid4()
models.UserInfo.objects.update_or_create(defaults={'token': token}, username=username)
msg['msg'] = '登入成功'
msg['token'] = token
msg['username'] = username
return Response(msg)
class Book(ModelViewSet):
authentication_classes = [util.TokenBaseAuthentication,]
permission_classes = [util.TokenBasePermission,]
queryset = models.Book.objects.all()
serializer_class = serializer.BookModelSerializer
class Publish(ModelViewSet):
authentication_classes = [util.TokenBaseAuthentication,]
permission_classes = [util.BasePermission,]
queryset = models.Publish.objects.all()
serializer_class = serializer.PublishModelSerializer