DRF Django REST framework 之 認證與權限組件(五)

引言

好久好久之前,Web站點只是做爲瀏覽服務器資源(數據)和其餘資源的工具,甚少有什麼用戶交互之類的煩人的事情須要處理,因此,Web站點的開發這根本不關心什麼人在何時訪問了什麼資源,不須要記錄任何數據,有客戶端請求,我即返回數據,簡單方便,每個http請求都是新的,響應以後當即斷開鏈接。django

而現在,互聯網的世界發生了翻天覆地的變化,用戶不只僅須要跟其餘用戶溝通交流,還須要跟服務器交互,不論是論壇類、商城類、社交類、門戶類仍是其餘各種Web站點,你們都很是重視用戶交互,只有跟用戶交互了,才能進一步留住用戶,只有留住了用戶,才能知道用戶需求,知道了用戶需求,纔會產生商機,有了用戶,就等於有了流量,纔可以騙到…額…是融到錢,有了資金企業才能繼續發展,可見,用戶交互是很是重要的,甚至能夠說是相當重要的一個基礎功能。瀏覽器

而談到用戶交互,則必需要談到咱們今天所要學習的知識點,認證、權限和頻率。首先咱們來看看認證。服務器

認證組件

使用token

大部分人都知道cookie和session這兩種方式能夠保存用戶信息,這兩種方式不一樣的是cookie保存在客戶端瀏覽器中,而session保存在服務器中,他們各有優缺點,配合起來使用,可將重要的敏感的信息存儲在session中,而在cookie中能夠存儲不太敏感的數據。cookie

而token稱之爲令牌。cookie、session和token都有其應用場景,沒有誰好誰壞,不過開發數據接口類的Web應用,目前用token仍是比較多的。session

token認證的大體步驟是這樣的:app

  • 用戶登陸,服務器端獲取用戶名密碼,查詢用戶表,若是存在該用戶且第一次登陸(或者token過時),生成token,不然返回錯誤信息
  • 若是不是第一次登陸,且token未過時,更新token值

定義 url dom

from django.urls import path, re_path

from DrfOne import views

urlpatterns = [

    path("books/", views.BookView.as_view({
        "get": "list",
        "post": "create",
    })),
    re_path('books/(?P<pk>\d+)/', views.BookView.as_view({
        'get': 'retrieve',
        'put': 'update',
        'delete': 'destroy'
    })),

    # 登錄
    path('login/', views.LoginView.as_view()),
]

建立兩個 model ,以下所示:ide

from django.db import models

class UserInfo(models.Model): username = models.CharField("姓名", max_length=32) password = models.CharField("密碼", max_length=32) age = models.IntegerField("年齡") gender = models.SmallIntegerField("性別", choices=((1, ""), (2, "")), default=1) user_type_entry = ((1, "普通用戶"), (2, "VIP"), (3, "SVIP")) user_type = models.SmallIntegerField("用戶級別", choices=user_type_entry) def __str__(self): return self.username class UserToken(models.Model): user = models.ForeignKey(to="UserInfo", on_delete=models.CASCADE) token = models.CharField(max_length=128)

 post 方法接口,視圖類以下所示:工具

from django.http import JsonResponse

from rest_framework.views import APIView

from DrfTwo.models import UserInfo, UserToken


class LoginView(APIView):
    def post(self, request):
        # 定義返回信息
        ret = dict()
        try:
            # 定義須要的信息
            fields = {"username", "password"}
            # 定義一個用戶信息字典
            user_info = dict()
            # 判斷fields是不是request.data的子集
            if fields.issubset(set(request.data.keys())):
                for key in fields:
                    user_info[key] = request.data.get(key)

            user_instance = UserInfo.objects.filter(**user_info).first()
            # 用戶驗證
            if user_instance:
                # 自定義generate_token()方法,獲取token值,在後面
                access_token = generate_token()
                # 用戶登錄成功,建立token,token存在更新token, defaults爲更新內容
                UserToken.objects.update_or_create(user=user_instance, defaults={
                    "token": access_token
                })
                ret["status_code"] = 200
                ret["status_message"] = "登陸成功"
                ret["access_token"] = access_token
                ret["user_role"] = user_instance.get_user_type_display()

            else:
                ret["status_code"] = 201
                ret["status_message"] = "登陸失敗,用戶名或密碼錯誤"
        except Exception as e:
            ret["status_code"] = 202
            ret["status_message"] = str(e)


        return JsonResponse(ret)

簡單寫了個獲取隨機字符串的方法用來生成token值:post

import uuid


def generate_token():
    random_str = str(uuid.uuid4()).replace("-", "")
    return random_str

以上就是token的簡單生成方式,固然,在生產環境中不會如此簡單,關於token也有相關的庫,而後在構造幾條數據以後,能夠經過POSTMAN工具來建立幾個用戶的token信息。

 

接下來,如何對已經登陸成功的用戶實現訪問受權呢?也就是說,只有登陸過的用戶(有token值)才能訪問特定的數據,該DRF的認證組件出場了

認證組件的使用

首先,新建一個認證類,以後的認證邏輯就包含在這個類裏面:

# 1.定義認證類
class UserAuth(object):
    # 認證邏輯
    def authenticate(self, request):
        user_token = request.GET.get('token')

        token_object = UserToken.objects.filter(token=user_token).first()
        if token_object:
            return token_object.user, token_object.token
        else:
            raise APIException("認證失敗")

實現方式看上去很是簡單,到 token 表裏面查看 token 是否存在,而後根據這個信息,返回對應信息便可,而後,在須要認證經過才能訪問的數據接口裏面註冊認證類便可:

from rest_framework.viewsets import ModelViewSet
from rest_framework.exceptions import APIException

from DrfOne.models import Book, UserToken
from DrfOne.drf_serializers import BookSerializer


# 1.定義認證類
class UserAuth(object):
    # 認證邏輯
    def authenticate(self, request):
        user_token = request.GET.get('token')

        token_object = UserToken.objects.filter(token=user_token).first()
        if token_object:
            return token_object.user.username, token_object.token
        else:
            raise APIException("認證失敗")


class BookView(ModelViewSet):
    # 2.指定認證類,固定寫法
    authentication_classes = [UserAuth]
    # 獲取數據源, 固定寫法
    queryset = Book.objects.all()
    # 序列化類, 固定寫法
    serializer_class = BookSerializer

序列類 BookSerializer 

from rest_framework import serializers

from DrfOne import models


class BookSerializer(serializers.ModelSerializer):
    class Meta:
        model = models.Book
        fields = "__all__"

        extra_kwargs = {
            # 僅寫
            "publish": {'write_only': True},
            "authors": {'write_only': True},
        }

    publish_name = serializers.CharField(max_length=32, read_only=True, source="publish.name")
    publish_address = serializers.CharField(max_length=32, read_only=True, source="publish.address")
    author_name = serializers.SerializerMethodField()

    def get_author_name(self, book_obj):
        author_list = list()
        for author in book_obj.authors.all():
            # 注意列表添加字段,author.name而不是author
            author_list.append(author.name)
        return author_list
類BookSerializer

多個認證類

須要注意的是,若是須要返回什麼數據,請在最後一個認證類中返回,由於若是在前面返回,在源碼的 self._authentication() 方法中會對返回值進行判斷,若是不爲空,認證的過程就會停止,多個認證類的實現方式以下:

# 1.定義認證類
class UserAuth(object):
    # 認證邏輯
    def authenticate(self, request):
        user_token = request.GET.get('token')

        token_object = UserToken.objects.filter(token=user_token).first()
        if token_object:
            return token_object.user.username, token_object.token
        else:
            raise APIException("認證失敗")


class UserAuthTwo(object):
    def authenticate(self, request):
        raise APIException("就是這麼簡單!")


class BookView(ModelViewSet):
    # 2.指定認證類,固定寫法
    authentication_classes = [UserAuth, UserAuthTwo]
    # 獲取數據源, 固定寫法
    queryset = models.Book.objects.all()
    # 序列化類, 固定寫法
    serializer_class = BookSerializer

全局認證

若是但願全部的數據接口都須要認證怎麼辦?很簡單,若是認證類本身沒有 authentication_classes ,就會到 settings 中去找,經過這個機制,咱們能夠將認證類寫入到 settings 文件中便可實現全局認證:

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'authenticator.views.UserAuth',
        'authenticator.views.UserAuthTwo',
    ),
}

權限組件

與認證組件幾乎差很少,只要判斷該用戶的級別,是否給予經過

權限組件的使用

定義權限類

 

from rest_framework.permissions import BasePermission


class UserPermission(BasePermission):
    message = "您沒有權限訪問該數據"
    def has_permission(self, request, view):
        # 用戶級別是否超過普通用戶
        if request.user.user_type > 1:
            return Truereturn False

 

一樣的邏輯,一樣的方式,只是執行權限的方法名與執行認證的方法名不同而已,名爲 has_permission ,而且須要將當前的視圖類傳遞給該方法。

視圖類中加入 permission_classes 變量:

class BookView(ModelViewSet):
    # 指定認證類,固定寫法
    authentication_classes = [UserAuth]
    # 指定權限類,固定寫法
    permission_classes = [UserPermission]
    # 獲取數據源, 固定寫法
    queryset = models.Book.objects.all()
    # 序列化類, 固定寫法
    serializer_class = BookSerializer

~>.<~

相關文章
相關標籤/搜索