04 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就变成了我们配置的一个个类的对象

image-20201124183749264

image-20201124183936492

image-20201124184336502

image-20201124184934710

image-20201124190921074

image-20201124191342474

image-20201124191639474

image-20201124192234947

image-20201124192426692

image-20201124192702274

image-20201124192908662

image-20201124193750187

image-20201124194156843

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)
            )

image-20201125083153477

image-20201125084227293

image-20201125083855951

image-20201125084047234

默认配置文件

image-20201125090632582

image-20201125092141817

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

原文地址:https://www.cnblogs.com/wait59/p/14207228.html