Django 的許可權系統是確保 Web 應用程式安全的重要組成部分。它允許開發者精細地控制哪些使用者可以存取哪些資源和執行哪些操作。理解 Django 的許可權和授權機制,對於構建安全的 Web 應用程式至關重要。本文將會詳細介紹 Django 的許可權系統,包括許可權的型別、如何建立超級使用者、如何檢查使用者許可權、如何管理使用者組和許可權,以及一些常見的授權陷阱和解決方案。透過理解這些概念,開發者可以更好地保護他們的應用程式,防止未經授權的存取和操作。

建立超級使用者和許可權

在 Django 中,許可權是最基本的授權單位。它授予使用者或使用者群組執行單一操作的許可權。要建立超級使用者和許可權,我們需要使用 Django 的管理命令。

# 建立超級使用者
from django.contrib.auth.models import User
user = User.objects.create_superuser('alice', 'alice@example.com', 'password')

管理使用者和群組許可權

在 Django 的管理控制檯中,我們可以管理使用者和群組的許可權。首先,我們需要登入管理控制檯。

# 登入管理控制檯
from django.contrib.auth import authenticate
user = authenticate(username='alice', password='password')

實施應用層級授權

Django 提供了多種方式來實施應用層級授權。其中一種方式是使用 permission_required 裝飾器。

# 使用 permission_required 裝飾器
from django.contrib.auth.decorators import permission_required
@permission_required('myapp.can_do_something')
def my_view(request):
    # 程式碼
    pass

測試授權邏輯

最後,我們需要測試授權邏輯,以確保它們按照預期工作。

# 測試授權邏輯
from django.test import TestCase
class AuthorizationTestCase(TestCase):
    def test_permission(self):
        # 程式碼
        pass

圖表翻譯:

  graph LR
    A[使用者] -->|登入|> B[管理控制檯]
    B -->|管理許可權|> C[使用者和群組]
    C -->|實施授權|> D[應用層級]
    D -->|測試授權|> E[授權邏輯]

在這個圖表中,我們可以看到使用者登入管理控制檯,管理使用者和群組的許可權,實施應用層級授權,最後測試授權邏輯。

應用層授權

在本節中,您將建立一個名為「訊息」(messaging)的新 Django 應用程式。這個應用程式將向您介紹 Django 授權的基本元素:許可權。要建立新的訊息應用程式,請在專案根目錄中執行以下命令。這個命令將在名為「messaging」的新目錄中生成一個 Django 應用程式:

python manage.py startapp messaging

生成的應用程式目錄結構如圖 10.1 所示。在這個練習中,您將向模型模組新增一個類別,並修改資料函式庫幾次,同時向遷移套件新增幾個專案。

圖 10.1 新 Django 應用程式「訊息」的目錄結構

現在,您需要將 Django 應用程式註冊到您的 Django 專案中。開啟設定模組,找到 INSTALLED_APPS 列表。新增以下以粗體字顯示的行,同時確保保留所有先前安裝的應用程式:

INSTALLED_APPS = [
    ...
    'messaging',
]

接下來,開啟 models.py 並在其中新增以下模型類別定義。AuthenticatedMessage 代表了一條訊息和一個雜湊值,具有兩個屬性。在第 14 章中,Alice 和 Bob 將使用這個類別進行安全通訊:

from django.db.models import Model, CharField

class AuthenticatedMessage(Model):
    message = CharField(max_length=100)
    hash_value = CharField(max_length=64)

如同所有模型一樣,AuthenticatedMessage 必須對映到一個資料函式庫表格。這個表格是透過 Django 遷移建立的。(您在前一章中學習了遷移。)這個對映是在執行時由 玄貓 的內建 ORM 框架處理的。

執行以下命令為您的模型類別生成一個遷移指令碼。這個命令將自動檢測新的模型類別並建立一個新的遷移指令碼,如下所示:

python manage.py makemigrations messaging

遷移สำหร ‘messaging’:

內容解密:

上述程式碼片段展示瞭如何建立一個新的 Django 應用程式,定義一個模型類別,並生成遷移指令碼。這些步驟是 Django 專案中建立應用程式和資料函式庫表格的基本流程。

圖表翻譯:

此圖表示了 Django 專案中建立應用程式和資料函式庫表格的流程。它包括建立新的 Django 應用程式、定義模型類別、生成遷移指令碼等步驟。這個流程是 Django 開發中的基本知識,對於建立和管理資料函式庫表格至關重要。

  flowchart TD
    A[建立 Django 應用程式] --> B[定義模型類別]
    B --> C[生成遷移指令碼]
    C --> D[建立資料函式庫表格]

Django 許可權系統入門

在 Django 中,許可權是透過內建的 Permission 模型來實作的。每個使用者可以與零到多個許可權相關聯。許可權可以分為兩類:預設許可權和自定義許可權。

預設許可權

預設許可權是由 Django 自動建立的,當您執行遷移指令碼時,它們就會被建立。這些許可權允許使用者建立、讀取、更新和刪除模型例項。例如,對於 AuthenticatedMessage 模型,Django 會自動建立四個預設許可權:

  • add_authenticatedmessage:允許使用者建立新的 AuthenticatedMessage 例項
  • change_authenticatedmessage:允許使用者更新現有的 AuthenticatedMessage 例項
  • delete_authenticatedmessage:允許使用者刪除現有的 AuthenticatedMessage 例項
  • view_authenticatedmessage:允許使用者檢視現有的 AuthenticatedMessage 例項

您可以使用 Django shell 來檢視所有預設許可權:

$ python manage.py shell
>>> from django.contrib.auth.models import Permission
>>> permissions = Permission.objects.filter(content_type__app_label='messaging', content_type__model='authenticatedmessage')
>>> permissions
['add_authenticatedmessage', 'change_authenticatedmessage', 'delete_authenticatedmessage', 'view_authenticatedmessage']

自定義許可權

自定義許可權是由您自己定義的,通常用於實作更細粒度的許可權控制。您可以在模型的 Meta 類別中定義自定義許可權。例如:

class AuthenticatedMessage(Model):
    message = CharField(max_length=100)
    mac = CharField(max_length=64)

    class Meta:
        permissions = [
            ('send_authenticatedmessage', 'Can send msgs'),
            ('receive_authenticatedmessage', 'Can receive msgs'),
        ]

自定義許可權也需要執行遷移指令碼才能建立。您可以使用以下命令來建立新的遷移指令碼:

$ python manage.py makemigrations messaging --name=add_permissions

然後,執行遷移指令碼:

$ python manage.py migrate

現在,您已經增加了一個新的應用程式、一個新的模型、一個新的資料函式庫表格和六個許可權到您的專案中。

使用者和群組管理

在這一節中,您將建立一個超級使用者 Alice。超級使用者是一種特殊的管理使用者,具有所有許可權。作為 Alice,您將存取 Django 的內建管理控制檯。這個控制檯是啟用在每個生成的 Django 專案中。一個簡短的管理控制檯巡覽將介紹您如何 Django 實作應用程式級別的授權。

Django 專案靜態內容服務

Django 專案可以更容易地使用和更好地呈現,如果可以提供靜態內容。Django 可以透過使用 WhiteNoise 來實作這一點,WhiteNoise 是一個設計用於高效地提供靜態內容的套件,同時最小化設定複雜性。

安裝 WhiteNoise

要安裝 WhiteNoise,請在虛擬環境中執行以下 pipenv 命令:

$ pipenv install whitenoise

啟用 WhiteNoise

要在 Django 中啟用 WhiteNoise,需要在 middleware 中新增 WhiteNoiseMiddleware。Middleware 是 Django 中的一個輕量級子系統,位於每個入站請求和檢視之間,以及檢視和每個出站回應之間。從這個位置,middleware 可以應用預處理和後處理邏輯。

要新增 WhiteNoiseMiddleware,請在設定模組中找到 MIDDLEWARE 設定,並新增 ‘whitenoise.middleware.WhiteNoiseMiddleware’ 到列表中。確保這個元件出現在 SecurityMiddleware 之後和其他元件之前。

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',
    # ...
]

重新啟動伺服器

重新啟動伺服器後,請在瀏覽器中指向管理主控臺登入頁面 https://localhost:8000/admin/。登入頁面應該出現,如圖 10.3 所示。如果瀏覽器渲染相同的表單而沒有樣式,則表示 WhiteNoise 沒有被安裝。這可能是由於 MIDDLEWARE 設定錯誤或伺服器沒有重新啟動。

圖表翻譯:

此圖示為 Django 的管理主控臺登入頁面,展示了 WhiteNoise 的作用。

  flowchart TD
    A[使用者請求] --> B[SecurityMiddleware]
    B --> C[WhiteNoiseMiddleware]
    C --> D[管理主控臺]
    D --> E[使用者]

內容解密:

此段程式碼展示瞭如何安裝和啟用 WhiteNoise。在虛擬環境中執行 pipenv 命令安裝 WhiteNoise,然後在設定模組中新增 WhiteNoiseMiddleware 到 MIDDLEWARE 列表中。重新啟動伺服器後,管理主控臺登入頁面將呈現正確的樣式。

Django 中的群組和許可權管理

在 Django 中,群組和許可權是用於控制使用者對應用程式中不同模型的存取許可權。群組可以將多個使用者與多個許可權相關聯,從而實作靈活的許可權管理。

建立超級使用者

要使用 Django 的管理控制檯,需要建立一個超級使用者。這可以透過執行以下命令實作:

python manage.py createsuperuser

這個命令會提示你輸入新超級使用者的密碼。

群組和許可權

群組是用於將多個使用者與多個許可權相關聯的。每個群組可以與零到多個許可權相關聯,也可以與零到多個使用者相關聯。每個與群組相關聯的許可權都會被授予該群組中的所有使用者。

在 Django 中,許可權分為四種:

  • can_add: 可以新增模型例項
  • can_change: 可以修改模型例項
  • can_delete: 可以刪除模型例項
  • can_view: 可以檢視模型例項

建立群組

要建立一個新群組,需要在管理控制檯中填寫群組名稱和選擇相關許可權。例如,要建立一個名為 “observers” 的群組,需要在 “Name” 欄中輸入 “observers”,然後選擇所有包含 “Can view” 的許可權。

程式化管理群組和許可權

除了在管理控制檯中手動管理群組和許可權外,也可以透過 Django 的 ORM 來實作。以下是示例程式碼:

from django.contrib.auth.models import User, Group, Permission

# 取得使用者
bob = User.objects.get(username='bob')

# 取得群組
observers = Group.objects.get(name='observers')

# 取得許可權
can_send = Permission.objects.get(codename='send_authenticated_message')

# 將使用者新增到群組中
bob.groups.add(observers)

# 將許可權新增到使用者中
bob.user_permissions.add(can_send)

這樣就可以實作對群組和許可權的程式化管理。

圖表翻譯:

  graph LR
    A[使用者] -->|新增到|> B[群組]
    B -->|授予|> C[許可權]
    C -->|實作|> D[許可權管理]

內容解密:

以上程式碼示範瞭如何在 Django 中建立超級使用者、管理群組和許可權。透過使用 Django 的 ORM,可以實作對群組和許可權的程式化管理,從而實作靈活的許可權控制。

使用Django的授權機制

在Django中,授權機制是用於控制使用者可以執行的操作。這包括了系統內的操作,例如讀取敏感資訊,和系統外的操作,例如指導飛行交通。Django提供了兩種方式來實作授權:低階的硬方式和高階的簡單方式。

低階的硬方式

Django的User模型提供了多種低階的方法,用於程式化的授權檢查。其中,has_perm方法可以用於存取預設和自訂的授權。以下例子中,Bob不允許建立其他使用者,但允許接收訊息:

>>> bob = User.objects.get(username='bob')
>>> bob.has_perm('auth.add_user')  # Bob不能新增使用者
False
>>> bob.has_perm('messaging.receive_authenticated_message')  # Bob可以接收訊息
True

has_perm方法對於超級使用者總是傳回True:

>>> alice = User.objects.get(username='alice')
>>> alice.is_superuser  # Alice是超級使用者
True
>>> alice.has_perm('auth.add_user')  # Alice可以新增使用者
True

has_perms方法提供了一種方便的方式來檢查多個授權:

>>> bob.has_perms(['auth.add_user', 'messaging.receive_authenticated_message'])  # Bob不能新增使用者和接收訊息
False
>>> bob.has_perms(['messaging.send_authenticated_message', 'messaging.receive_authenticated_message'])  # Bob可以傳送和接收訊息
True

雖然低階的API沒有問題,但應該盡量避免使用它,因為它需要更多的程式碼行數和更複雜的邏輯。

高階的簡單方式

Django提供了一種高階的簡單方式來實作授權,使用@permission_required裝飾器。這種方式更簡單、更易於使用和維護。以下例子中,使用@permission_required裝飾器來檢查使用者是否有許可權新增使用者:

from django.contrib.auth.decorators import permission_required

@permission_required('auth.add_user')
def add_user(request):
    # 程式碼
    pass

這種方式更簡單、更易於使用和維護,同時也提供了更好的安全性和靈活性。

使用者許可權檢查的潛在陷阱

在 Django 中,使用者許可權檢查是一個重要的安全機制,然而,如果不小心,可能會導致一些潛在的陷阱。以下是幾個例子:

錯誤的許可權檢查方法

使用 has_perm 方法檢查使用者許可權可能會導致錯誤。例如,如果您查詢一個不存在的許可權,它會簡單地傳回 False

>>> bob.has_perm('banana')
False

許可權快取問題

Django 的許可權系統會將許可權從資料函式庫中批次讀取並快取。這可能會導致一個危險的權衡。一方面,has_permhas_perms 不會在每次呼叫時觸發資料函式庫查詢。另一方面,您需要小心地檢查許可權,特別是在將許可權應用到使用者之後。

以下的程式碼片段示範了這個問題:

>>> perm = 'messaging.send_authenticatedmessage'
>>> bob.has_perm(perm)
True

>>> can_send = Permission.objects.get(codename='send_authenticatedmessage')
>>> bob.user_permissions.remove(can_send)

>>> bob.has_perm(perm)
True

如您所見,雖然我們已經從 Bob 中移除了許可權,但是 has_perm 方法仍然傳回 True。這是因為本地的許可權狀態沒有被更新。

更新使用者許可權狀態

要更新使用者許可權狀態,您需要重新從資料函式庫中讀取使用者物件:

>>> bob.refresh_from_db()
>>> bob.has_perm(perm)
True

>>> reloaded = User.objects.get(id=bob.id)
>>> reloaded.has_perm(perm)
False

許可權檢查的陷阱

以下的程式碼片段定義了一個檢視,該檢視在渲染敏感資訊之前執行許可權檢查。然而,它有兩個 bug。您能夠找到它們嗎?

from django.shortcuts import render
from django.views import View

class UserView(View):
    def get(self, request):
        assert request.user.has_perm('auth.view_user')
        # ...

內容解密:

在這個例子中,檢視使用 assert 陳述式來檢查使用者許可權。如果使用者沒有許可權,則會引發一個 AssertionError。然而,這不是一個好的做法,因為它會導致檢視當機。

一個更好的方法是使用 PermissionDenied 例外來處理許可權檢查失敗的情況:

from django.core.exceptions import PermissionDenied

class UserView(View):
    def get(self, request):
        if not request.user.has_perm('auth.view_user'):
            raise PermissionDenied
        # ...

這樣,檢視就可以正確地處理許可權檢查失敗的情況,並傳回一個 403 頁面給使用者。

圖表翻譯:

以下是檢視的流程圖:

  flowchart TD
    A[使用者請求] --> B[許可權檢查]
    B --> C{有許可權}
    C -->|是| D[渲染敏感資訊]
    C -->|否| E[引發PermissionDenied]
    E --> F[傳回403頁面]

這個流程圖顯示了檢視的流程,包括許可權檢查和處理許可權檢查失敗的情況。

Django 中的授權機制: PermissionRequiredMixin 和 permission_required

在 Django 中,授權機制是用於控制使用者對特定資源的存取許可權。其中,PermissionRequiredMixinpermission_required 是兩種常用的授權機制。

PermissionRequiredMixin

PermissionRequiredMixin 是一個 mixin 類別,用於授權個別檢視(views)。它會自動檢查每個傳入請求的使用者許可權。您可以使用 permission_required 屬性指定要檢查的許可權。這個屬性可以是一個字串,代表一個許可權,也可以是一個可迭代的字串,代表多個許可權。

例如:

from django.contrib.auth.mixins import PermissionRequiredMixin
from django.views import View

class AuthenticatedMessageView(PermissionRequiredMixin, View):
    permission_required = 'messaging.view_authenticatedmessage'

在這個例子中,AuthenticatedMessageView 檢視繼承自 PermissionRequiredMixin,並指定了 permission_required 屬性為 'messaging.view_authenticatedmessage'。這意味著只有具有 'messaging.view_authenticatedmessage' 許可權的使用者才能存取這個檢視。

permission_required

permission_required 是一個 decorator,用於授權特定函式或方法。它會檢查使用者是否具有指定的許可權,如果沒有,則會引發一個 PermissionDenied 例外。

例如:

from django.contrib.auth.decorators import permission_required

@permission_required('messaging.view_authenticatedmessage')
def authenticated_message_view(request):
    # ...

在這個例子中,authenticated_message_view 函式使用 permission_required decorator,指定了 'messaging.view_authenticatedmessage' 許可權。只有具有這個許可權的使用者才能存取這個函式。

403 狀態碼

當使用者沒有授權存取某個資源時,Django 會傳回一個 403 狀態碼,表示「Forbidden」。這個狀態碼告訴使用者,該資源是禁止存取的。

例如:

from django.http import HttpResponseForbidden

def authenticated_message_view(request):
    if not request.user.has_perm('messaging.view_authenticatedmessage'):
        return HttpResponseForbidden()
    # ...

在這個例子中,如果使用者沒有 'messaging.view_authenticatedmessage' 許可權,則會傳回一個 403 狀態碼。

使用 Django 實作授權和驗證

在 Django 中,授權和驗證是兩個非常重要的概念。授權是指確定使用者是否有權存取某個資源或執行某個動作,而驗證是指確認使用者的身份。在本節中,我們將探討如何使用 Django 的內建工具實作授權和驗證。

使用 PermissionRequiredMixin

PermissionRequiredMixin 是 Django 中的一個 mixin,用於檢查使用者是否具有某個許可權。它可以與類別基礎的檢視(Class-Based View)一起使用。以下是使用 PermissionRequiredMixin 的範例:

from django.views import View
from django.http import JsonResponse
from django.contrib.auth.mixins import PermissionRequiredMixin

class MyView(PermissionRequiredMixin, View):
    permission_required = 'myapp.view_my_model'

    def get(self, request):
        # ...
        return JsonResponse(data)

在這個範例中,MyView 類別繼承自 PermissionRequiredMixinViewpermission_required 屬性指定了需要檢查的許可權。如果使用者沒有這個許可權,Django 將傳回 403 頁面。

使用 @permission_required 裝飾器

@permission_required 裝飾器是 PermissionRequiredMixin 的函式基礎的檢視(Function-Based View)版本。它可以用來檢查使用者是否具有某個許可權。以下是使用 @permission_required 裝飾器的範例:

from django.http import JsonResponse
from django.contrib.auth.decorators import permission_required

@permission_required('myapp.view_my_model', raise_exception=True)
def my_view(request):
    # ...
    return JsonResponse(data)

在這個範例中,my_view 函式使用 @permission_required 裝飾器檢查使用者是否具有 myapp.view_my_model 許可權。如果使用者沒有這個許可權,Django 將傳回 403 頁面。

使用 UserPassesTestMixin

UserPassesTestMixin 是 Django 中的一個 mixin,用於檢查使用者是否滿足某個條件。它可以與類別基礎的檢視(Class-Based View)一起使用。以下是使用 UserPassesTestMixin 的範例:

from django.views import View
from django.http import JsonResponse
from django.contrib.auth.mixins import UserPassesTestMixin

class MyView(UserPassesTestMixin, View):
    def test_func(self):
        user = self.request.user
        return user.date_joined.year > 2020 or user.username == 'alice'

    def get(self, request):
        # ...
        return JsonResponse(data)

在這個範例中,MyView 類別繼承自 UserPassesTestMixinViewtest_func 方法用來檢查使用者是否滿足某個條件。如果使用者不滿足這個條件,Django 將傳回 403 頁面。

使用 @user_passes_test 裝飾器

@user_passes_test 裝飾器是 UserPassesTestMixin 的函式基礎的檢視(Function-Based View)版本。它可以用來檢查使用者是否滿足某個條件。以下是使用 @user_passes_test 裝飾器的範例:

from django.http import JsonResponse
from django.contrib.auth.decorators import user_passes_test

def test_func(user):
    return user.date_joined.year > 2020 or user.username == 'alice'

@user_passes_test(test_func, login_url='/login/')
def my_view(request):
    # ...
    return JsonResponse(data)

在這個範例中,my_view 函式使用 @user_passes_test 裝飾器檢查使用者是否滿足某個條件。如果使用者不滿足這個條件,Django 將傳回 403 頁面。

使用 Django 的授權機制

Django 提供了多種授權機制,包括根據使用者的授權和根據角色的授權。根據使用者的授權是透過 @user_passes_test 裝飾器或 UserPassesTestMixin 類別來實作的。這些機制可以用來限制使用者存取某些檢視或資源。

從系統安全形度來看,本文深入探討了 Django 框架提供的授權機制,涵蓋了從建立超級使用者、管理許可權到實施應用層級授權的完整流程。藉由分析 permission_required 裝飾器、PermissionRequiredMixinUserPassesTestMixin 等工具,我們可以發現 Django 提供了相當精細且彈性的授權控制,能有效保障系統安全。然而,許可權快取問題和錯誤的許可權檢查方法等潛在陷阱也需要開發者特別注意,例如使用 refresh_from_db() 更新許可權狀態,避免安全漏洞。對於追求高效開發的團隊,建議優先採用 PermissionRequiredMixin@permission_required 等高階 API,簡化程式碼並提升可維護性。展望未來,隨著網路安全威脅日益複雜,預期 Django 授權機制將持續演進,整合更進階的安全策略,例如根據角色的存取控制(RBAC)和屬性根據存取控制(ABAC),以滿足更精細的授權需求。對於開發者而言,持續關注並學習這些新興技術,才能打造更安全可靠的網路應用程式。 因此,妥善運用 Django 的授權機制,並持續提升安全意識,是保障 Web 應用程式安全的關鍵。