python測試開發django-61.許可權認證(permission)

  • 2019 年 10 月 4 日
  • 筆記

前言

用戶登錄後,才有操作當前用戶的許可權,不能操作其它人的用戶,這就是需要用到許可權認證,要不然你登錄自己的用戶,去操作別人用戶的相關數據,就很危險了。

  • authentication是身份認證,判斷當前用戶的登錄方式是哪種認證方式
  • permissions 是許可權認證,判斷哪些用戶有操作許可權

authentication身份認證

身份驗證是將收到的請求和一組標識證書(如用戶名密碼、令牌)進行關聯的一種機制,以便許可權和策略可以根據這個標識證書來決定是否允許該請求。因此,身份驗證發生在驗證許可權和限制檢查之前。

當收到的請求通過身份驗證時:

  • request.user屬性會設置為django.contrib.auth.User對象,即我們登錄的對象(我們定義用戶繼承於User)。
  • request.auth會設置為對應的Token(如果帶有Token)或者None(如果不帶有Token)。 當收到請求身份驗證失敗時:
  • request.user屬性會設置為django.contrib.auth.models.AnonymousUser對象。
  • request.auth會設置為None。

django rest framework許可權和認證有四種方式:

  • BasicAuthentication 此身份驗證方案使用HTTP基本身份驗證,根據用戶的用戶名和密碼進行簽名。基本身份驗證通常僅適用於測試
  • TokenAuthentication 此身份驗證方案使用基於令牌的簡單HTTP身份驗證方案。令牌認證適用於客戶端 – 伺服器設置,例如本機桌面和移動客戶端。
  • SessionAuthentication 此身份驗證方案使用Django的默認會話後端進行身份驗證。會話身份驗證適用於與您的網站在同一會話上下文中運行的AJAX客戶端。
  • RemoteUserAuthentication 此身份驗證方案允許您將身份驗證委派給Web伺服器,該伺服器設置REMOTE_USER 環境變數。

permission許可權認證

許可權檢查通常使用request.user和request.auth屬性中的身份驗證資訊來確定是否應允許傳入請求。

當許可權檢查失敗時,將根據以下規則返回HTTP 403 Forbidden或HTTP 401 Unauthorized:

  • 如果收到的請求身份驗證通過,但是許可權驗證失敗,則返回HTTP 403 Forbidden;
  • 如果收到的請求身份驗證失敗,且最高優先順序驗證類不能使用WWW-Authenticate請求頭,則返回HTTP 403 Forbidden;
  • 如果收到的請求身份驗證失敗,且最高優先順序驗證類可以使用WWW-Authenticate請求頭,則返回HTTP 401 Unauthorized

許可權級別也有四種

  • AllowAny 允許所有用戶
  • IsAuthenticated 表示僅僅允許身份驗證通過的用戶訪問,其他用戶無法訪問。
  • IsAdminUser 表示僅僅允許管理員用戶訪問,普通用戶無法訪問。
  • IsAuthenticatedOrReadOnly 表示僅僅允許身份驗證通過的用戶訪問,或者只允許只讀請求(GET請求)訪問。

相關配置

在settings.py中,INSTALLED_APPS添加rest_framework和rest_framework.authtoken

INSTALLED_APPS = [      'apiapp',      'rest_framework.authtoken',      'rest_framework',  ]

REST_FRAMEWORK添加許可權認證方式和身份認證方式

REST_FRAMEWORK = {      # 許可權認證      'DEFAULT_PERMISSION_CLASSES': (          'rest_framework.permissions.IsAuthenticated',            # IsAuthenticated 僅通過認證的用戶          'rest_framework.permissions.AllowAny',                   # AllowAny 允許所有用戶          'rest_framework.permissions.IsAdminUser',                # IsAdminUser 僅管理員用戶          'rest_framework.permissions.IsAuthenticatedOrReadOnly',  # IsAuthenticatedOrReadOnly 認證的用戶可以完全操作,否則只能get讀取      ),      # 身份認證      'DEFAULT_AUTHENTICATION_CLASSES': (          'rest_framework.authentication.BasicAuthentication',          'rest_framework.authentication.SessionAuthentication',          'rest_framework.authentication.TokenAuthentication',  # token認證      )  }

登錄生成token

登錄的時候,不需要身份認證,permission_classes設置成AllowAny,允許所有的用戶

permission_classes = (AllowAny,) # AllowAny 允許所有用戶

from django.http import JsonResponse  from django.shortcuts import HttpResponse  from rest_framework.authtoken.models import Token  from django.contrib import auth  from rest_framework.views import APIView  from rest_framework import viewsets  from rest_framework import serializers  from .models import *  from django.http import QueryDict  from rest_framework.request import Request  from rest_framework.permissions import IsAuthenticated,AllowAny  from rest_framework.authentication import TokenAuthentication    '''作者:上海悠悠,QQ交流群:750815713'''    class LoginViewSet(APIView):      '''登錄獲取token方法'''      permission_classes = (AllowAny,)      # AllowAny 允許所有用戶        def post(self, request, *args, **kwargs):          username = request.data.get('username')          # print(username)          password = request.data.get('password')          user = auth.authenticate(username=username, password=password)          if not user:              return HttpResponse({"code": 0,                                  "msg": "用戶名或密碼不對!"})          # 刪除原有的Token          old_token = Token.objects.filter(user=user)          old_token.delete()          # 創建新的Token          token = Token.objects.create(user=user)          return JsonResponse({"code": 0,                               "msg": "login success!",                               "username": user.username,                               "token": token.key})

添加card相關資訊,接著前面一篇講的,添加authentication_classes和permission_classes

authentication_classes = (TokenAuthentication,) # token認證 permission_classes = (IsAuthenticated,) # # IsAuthenticated 僅通過認證的用戶

def get_parameter_dic(request, *args, **kwargs):      # 作者:上海悠悠,QQ交流群:750815713      if isinstance(request, Request) == False:          return {}        query_params = request.query_params      if isinstance(query_params, QueryDict):          query_params = query_params.dict()      result_data = request.data      if isinstance(result_data, QueryDict):          result_data = result_data.dict()        if query_params != {}:          return query_params      else:          return result_data    class CardSerializer(serializers.HyperlinkedModelSerializer):      class Meta:          model = Card          fields = "__all__"    class CardViewSet(viewsets.ModelViewSet):      '''# 作者:上海悠悠,QQ交流群:750815713'''      authentication_classes = (TokenAuthentication,)   # token認證      permission_classes = (IsAuthenticated,)  # # IsAuthenticated 僅通過認證的用戶      queryset = Card.objects.all()      serializer_class = CardSerializer        def get(self, request, *args, **kwargs):          params=get_parameter_dic(request)          return JsonResponse(data=params)        def post(self, request, *args, **kwargs):          params=get_parameter_dic(request)          return JsonResponse(data=params)        def put(self, request, *args, **kwargs):          params=get_parameter_dic(request)          return JsonResponse(data=params)

models.py設計card表

class Card(models.Model):      '''銀行卡 基本資訊 # 作者:上海悠悠,QQ交流群:750815713'''      card_id = models.CharField(max_length=30, verbose_name="卡號", default="")      card_user = models.CharField(max_length=10, verbose_name="姓名", default="")      add_time = models.DateField(auto_now=True, verbose_name="添加時間")        class Meta:          verbose_name_plural = '銀行卡賬戶'          verbose_name = "銀行卡賬戶_基本資訊"        def __str__(self):          return self.card_id

urls.py添加方法地址

from apiapp import views  from django.conf.urls import url  from rest_framework import routers  from django.conf.urls import include    # 作者:上海悠悠,QQ交流群:750815713    router = routers.DefaultRouter()  router.register(r'cards', views.CardViewSet)    urlpatterns = [      url(r'^api/v1/login/$', views.LoginViewSet.as_view()),      url(r'^', include(router.urls)),  ]

測試介面

先獲取登錄token,把token值複製出來:1c0debb44fa0054d312616e7000ae78ce396df8e

{      "code": 0,      "msg": "login success!",      "username": "test",      "token": "1c0debb44fa0054d312616e7000ae78ce396df8e"  }

訪問添加銀行卡帳號的介面時,需在頭部帶上token,格式為

Authorization: Token 1c0debb44fa0054d312616e7000ae78ce396df8e

帶上token去請求的時候,就可以正常的添加成功

查看資料庫card表會有數據新增成功

如果token錯誤,或者沒有token就會出現401 Unauthorized