6-1 认证和权限-DRF认证

目录:

  • 认证介绍
  • 初始化操作(模型、视图、urls)
  • 初识BasicAuthentication(客户端用户名密码认证)

一、DRF认证

  现在我们写的接口,会发现一个问题,就是任何人都可以创建数据库,修改数据。这样肯定是不行的,我们希望只有数据的创建者才能有权限修改数据,如果不是,只能有只读权限。这个好比什么呐?好比我们有一个购物车的话,去访问,我们就要去登录的情况下去访问。

其实在Django rest famework 已经默认帮我做认证了。我们可以去 看下源码:我们按 Ctrl + 类名:

generics.ListCreateAPIView  =>  GenericAPIView => views.APIView => APIView(View)

来我们看下 APIView 的源码:

class APIView(View):
    ....
    authentication_classes = api_settings.DEFAULT_AUTHENTICATION_CLASSES   #权限认证
    ....

我们进入 Ctrl + api_settings 继续进去看:

api_settings = APISettings(None, DEFAULTS, IMPORT_STRINGS)  #原来在这边,意思就是说 有配置权限的话就去读自己配置的,没有的话就读DEFAULTS

好啦!那我们进去看看  Ctrl + DEFAULTS 继续看看,默认的权限是啥?

EFAULTS = {
    # Base API policies
    .....,
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication', #session验证
        'rest_framework.authentication.BasicAuthentication' #客户端 用户名密码验证
    ),
    ....
}

好啦。终于明白啦。它的默认验证是  SessionAuthentication 和 BasicAuthentication。

二、初始化操作

2.1、创建app

python manage.py startapp app04

2.2、模型

说明:编辑modles.py文件

from django.db import models

class Game(models.Model):
    name = models.CharField(verbose_name="游戏名", max_length=64)
    desc = models.CharField(verbose_name="描述", max_length=20)
    user = models.ForeignKey('auth.User', on_delete=models.CASCADE)  #继承django自带的用户

    def __str__(self):
        return self.name

2.3、序列化

说明:新建并且编辑 serializers.py 文件

from rest_framework import serializers
from .models import Game

class GameSerializer(serializers.ModelSerializer):
    class Meta:
        model = Game
        fields = "__all__"

        # extra_kwargs = {
        #     "user": {"read_only": True}  #当我们开启验证的时候需要打开,因为默认是 当前用户。所以只需 出参显示,入参无需传入
        # }

2.4、路由

说明:编辑根级路由和当前路由

#根级路由
from django.contrib import admin
from django.urls import path, include


urlpatterns = [
    path('admin/', admin.site.urls),
    path('api/', include('app04.urls'))
]

#当前app04路由
from django.urls import path
from app04 import views


urlpatterns = [
    path("game/", views.GameList.as_view(), name="game-list"),
    path("game/<int:pk>/", views.GameDetail.as_view(), name="game-detail")
]

2.5、 初始化数据库

#初始化数据库
>python manage.py makemigrations

>python manage.py migrate

#新建用户
>python manage.py createsuperuser

2.6、视图

说明:编辑Views.py视图文件

from .serializers import GameSerializer
from .models import Game
from rest_framework import generics

class GameList(generics.ListCreateAPIView):  #ListCreateAPIView=>GenericAPIView => views.APIView => APIView(View)

    queryset = Game.objects.all()
    serializer_class = GameSerializer


class GameDetail(generics.RetrieveUpdateDestroyAPIView):

    queryset = Game.objects.all()
    serializer_class = GameSerializer

三、初识BasicAuthentication(客户端用户名密码认证)

3.1、 BasicAuthentication验证

说明:因为我们创建用户的时候,需要 获取当前的用户,不需要自己输入,所以就是在我们创建保存数据库的时候需要重写对应的方法。那这个函数在哪里呐。我们找一下。

Ctrl + ListCreateAPIView => CreateModelMixin

class CreateModelMixin(object):
    """
    Create a model instance.
    """
    def create(self, request, *args, **kwargs):
        serializer = self.get_serializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        self.perform_create(serializer)
        headers = self.get_success_headers(serializer.data)
        return Response(serializer.data, status=status.HTTP_201_CREATED, headers=headers)

    def perform_create(self, serializer):
        serializer.save()

    def get_success_headers(self, data):
        try:
            return {'Location': str(data[api_settings.URL_FIELD_NAME])}
        except (TypeError, KeyError):
            return {}
CreateModelMixin源码

能看出来,我们在保存序列化数据的时候是 通过 perform_create 方法保存数据。所以我们只需要重写这个方法就可以了。

所以我们编辑 views.py中的GameList类。如下:

from .serializers import GameSerializer
from .models import Game
from rest_framework import generics
from rest_framework.authentication import BasicAuthentication   #导入BasicAuthentication认证,是用于客户端用户名密码认证


class GameList(generics.ListCreateAPIView):

    queryset = Game.objects.all()
    serializer_class = GameSerializer

    def perform_create(self, serializer):  #重写perform_create方法,传入当前用户
        #print(self.request.user)  #验证查看是否是当前用户
        serializer.save(user=self.request.user)  #request是APIview封装好的,这个request.user是BasicAuthentication认证赋值的

查看一下 self.request的源码,看看Apiview到底是怎么封装的:Ctrl 按键 + request:

class APIView(View):
    ....
    def dispatch(self, request, *args, **kwargs):
        ....
        #对django的request的进行封装,在 initialize_request 做了用户认证,然后把 BasicAuthentication 传进去了,可以 Crtrl + initialize_request进去看
        request = self.initialize_request(request, *args, **kwargs)

        #封装后的 request 赋值给 APIView
        self.request = request
        ....

好啦,request知道是哪里来的了,那我们再看看  BasicAuthentication 到底返回给我们什么?看下 BasicAuthentication的源码:

class BasicAuthentication(BaseAuthentication):
    """
    HTTP Basic authentication against username/password.
    """
    www_authenticate_realm = 'api'

    def authenticate(self, request):
        """
        Returns a `User` if a correct username and password have been supplied
        using HTTP Basic authentication.  Otherwise returns `None`.
        """
        auth = get_authorization_header(request).split()

        if not auth or auth[0].lower() != b'basic':
            return None

        if len(auth) == 1:
            msg = _('Invalid basic header. No credentials provided.')
            raise exceptions.AuthenticationFailed(msg)
        elif len(auth) > 2:
            msg = _('Invalid basic header. Credentials string should not contain spaces.')
            raise exceptions.AuthenticationFailed(msg)

        try:
            auth_parts = base64.b64decode(auth[1]).decode(HTTP_HEADER_ENCODING).partition(':')
        except (TypeError, UnicodeDecodeError, binascii.Error):
            msg = _('Invalid basic header. Credentials not correctly base64 encoded.')
            raise exceptions.AuthenticationFailed(msg)

        userid, password = auth_parts[0], auth_parts[2]
        return self.authenticate_credentials(userid, password, request)

    def authenticate_credentials(self, userid, password, request=None):
        """
        Authenticate the userid and password against username and password
        with optional request for context.
        """
        credentials = {
            get_user_model().USERNAME_FIELD: userid,
            'password': password
        }
        user = authenticate(request=request, **credentials)

        if user is None:
            raise exceptions.AuthenticationFailed(_('Invalid username/password.'))

        if not user.is_active:
            raise exceptions.AuthenticationFailed(_('User inactive or deleted.'))

        return (user, None)   #返回出来了user=>user给了request,none给了token

    def authenticate_header(self, request):
        return 'Basic realm="%s"' % self.www_authenticate_realm
BasicAuthentication源码

对了,这边别忘记了  serializers.py 序列化文件也要开放一下:

from rest_framework import serializers
from .models import Game

class GameSerializer(serializers.ModelSerializer):
    class Meta:
        model = Game
        fields = "__all__"

        extra_kwargs = {
            "user": {"read_only": True}  #由于用户是出参显示,入参只需要当前用户,所以这边设置只读
        }

好啦。这边我们事先 准备了一个用户 :zhangqg,密码:123456,通过 createsuperuser命令创建。

postman测试步骤:

1、入参:

 

2、 Basic Auth 用户名密码验证

3.2、认证总结

  • DRF默认认证是 SessionAuthentication、BasicAuthentication
  • 目前采用BasicAuthentication、通过浏览器传递账号密码,base64加密,认证成功后,返回一个元组。元组的第一个用户对象,赋值给request
  • 我们重写perform_create 就可以从request 拿到User
  • 在序列化话里面取当前用户 => HiddenField ,serializer.CurrentUserDefault(源码显示还是一个当前request)
posted @ 2020-04-28 11:59  帅丶高高  阅读(449)  评论(0)    收藏  举报