1. 程式人生 > 實用技巧 >drf 認證,許可權

drf 認證,許可權

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