在 Django 框架中,有效管理使用者許可權是構建安全 Web 應用程式的關鍵。本文將深入探討 Django 的使用者、群組與授權機制,並提供程式碼範例,涵蓋從基本概念到進階技巧的實務運用。我們將探討如何使用 PermissionRequiredMixin@permission_required 裝飾器來控制使用者對特定資源的存取,同時也將介紹如何避免使用 assert 進行授權檢查,以及如何正確更新使用者的 Permission 狀態以避免快取問題。此外,我們還將探討 Django 範本中根據授權的條件顯示,以及如何測試授權機制。最後,我們將簡要介紹 OAuth 2.0 授權框架,並說明其在 Django 中的應用。

使用者群組和授權

在 Django 中,使用者可以被分配到不同的群組中,每個群組都有一些特定的授權。使用者也可以被直接授予某些授權。

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

# 取得觀察者群組的例項
observers = Group.objects.get(name='observers')

# 取得傳送驗證訊息的授權
can_send = Permission.objects.get(codename='send_authenticatedmessage')

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

# 將傳送驗證訊息的授權新增到使用者 bob
bob.user_permissions.add(can_send)

復原使用者群組和授權

使用者也可以被從群組中移除,或者復原某些授權。

# 將使用者 bob 從觀察者群組中移除
bob.groups.remove(observers)

# 復原使用者 bob 的傳送驗證訊息授權
bob.user_permissions.remove(can_send)

強制授權

Django 中的強制授權是一種機制,能夠控制使用者對系統的存取和操作。有兩種方式可以實作強制授權:低階別和高階別。

低階別強制授權

使用者模型有幾個低階別的方法,可以用於程式化地檢查授權。

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

# 檢查使用者 bob 是否有新增使用者的授權
print(bob.has_perm('auth.add_user'))  # False

# 檢查使用者 bob 是否有接收驗證訊息的授權
print(bob.has_perm('messaging.receive_authenticatedmessage'))  # True

高階別強制授權

Django 也提供了一種高階別的方式來實作強制授權。

# 取得使用者 alice 的例項
alice = User.objects.get(username='alice')

# 檢查使用者 alice 是否為超級使用者
print(alice.is_superuser)  # True

# 檢查使用者 alice 是否有新增使用者的授權
print(alice.has_perm('auth.add_user'))  # True

多個授權檢查

可以使用 has_perms 方法來檢查多個授權。

# 檢查使用者 bob 是否有新增使用者和接收驗證訊息的授權
print(bob.has_perms(['auth.add_user', 'messaging.receive_authenticatedmessage']))  # False

使用 Django 的 Permission 系統進行授權管理

在 Django 中,Permission 是一個強大的工具,用於控制使用者對模型的存取許可權。然而,在使用 Permission 時,需要注意一些潛在的陷阱,以避免出現錯誤。

低階 API 的使用

雖然 Django 提供了低階 API 來進行 Permission 檢查,但這種方法並不推薦。低階 API 的檢查需要更多的程式碼,並且容易出現錯誤。例如,如果在請求中傳遞一個不存在的 Permission,API 會簡單地傳回 False,而不會拋出任何錯誤。

Permission 的快取

Django 的 Permission 系統使用快取來儲存使用者的 Permission 狀態。這可以提高效率,但也可能導致問題。如果使用者的 Permission 狀態發生變化,快取可能不會立即更新。例如,如果使用者被授予一個新的 Permission,然後立即檢查該 Permission,可能會傳回錯誤的結果。

更新使用者的 Permission 狀態

如果使用者的 Permission 狀態發生變化,需要更新使用者的快取。可以使用 refresh_from_db() 方法來更新使用者的快取,但這可能不夠。為了確保得到最新的 Permission 狀態,需要重新載入使用者的模型。

範例

以下範例示範瞭如何使用 Django 的 Permission 系統進行授權管理:

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

# 建立一個使用者
bob = User.objects.create_user('bob', 'bob@example.com', 'password')

# 授予使用者一個 Permission
perm = Permission.objects.get(codename='messaging.send_authenticatedmessage')
bob.user_permissions.add(perm)

# 檢查使用者是否具有該 Permission
print(bob.has_perm(perm))  # True

# 復原使用者的 Permission
can_send = Permission.objects.get(codename='send_authenticatedmessage')
bob.user_permissions.remove(can_send)

# 檢查使用者是否具有該 Permission
print(bob.has_perm(perm))  # True (錯誤的結果)

# 更新使用者的快取
bob.refresh_from_db()

# 檢查使用者是否具有該 Permission
print(bob.has_perm(perm))  # True (錯誤的結果)

# 重新載入使用者的模型
reloaded = User.objects.get(id=bob.id)

# 檢查使用者是否具有該 Permission
print(reloaded.has_perm(perm))  # False (正確的結果)

在這個範例中,我們建立了一個使用者 bob,並授予他一個 messaging.send_authenticatedmessage 的 Permission。然後,我們復原了他的 Permission,但他的快取仍然顯示他具有該 Permission。直到我們重新載入他的模型,才得到正確的結果。

根據提供的內容,這是一篇關於 Django 框架中實作授權(Authorization)的文章。以下是對這篇文章的重寫和總結:

Django 中的授權

在 Django 中,授權是確保只有具備適當許可的使用者才能存取特定資源或執行特定動作的過程。這篇文章將介紹如何在 Django 中實作授權,包括使用 PermissionRequiredMixin@permission_required 裝飾器。

使用 assert 陳述式進行授權

在 Django 中,使用 assert 陳述式來進行授權是不可靠的。因為當 Python 以最佳化模式執行時,assert 陳述式將被忽略。以下是錯誤示範:

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')
        #...

這種方法不僅不可靠,還可能導致安全漏洞。

使用 PermissionRequiredMixin 進行授權

PermissionRequiredMixin 是 Django 提供的一個 mixin 類別,用於簡化授權的實作。它可以自動檢查使用者是否具有特定的許可。以下是正確示範:

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

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

    def get(self, request):
        #...

在這個示範中,PermissionRequiredMixin 會自動檢查使用者是否具有 messaging.view_authenticatedmessage 許可。如果使用者沒有此許可,則會傳回 403 狀態碼。

使用 @permission_required 裝飾器進行授權

另一個實作授權的方法是使用 @permission_required 裝飾器。這個裝飾器可以用於檢查使用者是否具有特定的許可。以下是示範:

from django.contrib.auth.decorators import permission_required

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

這個裝飾器會自動檢查使用者是否具有 messaging.view_authenticatedmessage 許可。如果使用者沒有此許可,則會傳回 403 狀態碼。

Django 中的授權機制

Django 提供了多種授權機制,以確保只有具備適當許可的使用者才能存取特定的資源。以下是 Django 中幾種常見的授權機制:

1. PermissionRequiredMixin

PermissionRequiredMixin 是一個類別基礎的 mixin,用於檢查使用者是否具有特定的許可。它可以與類別基礎的檢視(Class-Based View)一起使用,以確保只有具有適當許可的使用者才能存取檢視。

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

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

    def get(self, request):
        # 只有具有 myapp.my_permission 許可的使用者才能存取此檢視
        return JsonResponse({'message': 'Hello, world!'})

2. @permission_required

@permission_required 是一個裝飾器,用於檢查使用者是否具有特定的許可。它可以與函式基礎的檢視(Function-Based View)一起使用,以確保只有具有適當許可的使用者才能存取檢視。

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

@permission_required('myapp.my_permission')
def my_view(request):
    # 只有具有 myapp.my_permission 許可的使用者才能存取此檢視
    return JsonResponse({'message': 'Hello, world!'})

3. UserPassesTestMixin

UserPassesTestMixin 是一個 mixin,用於檢查使用者是否透過了一個自訂的測試。它可以與類別基礎的檢視一起使用,以確保只有透過測試的使用者才能存取檢視。

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

class MyView(UserPassesTestMixin, View):
    def test_func(self):
        # 自訂測試邏輯
        return self.request.user.date_joined.year > 2020 or self.request.user.username == 'alice'

    def get(self, request):
        # 只有透過測試的使用者才能存取此檢視
        return JsonResponse({'message': 'Hello, world!'})

4. @user_passes_test

@user_passes_test 是一個裝飾器,用於檢查使用者是否透過了一個自訂的測試。它可以與函式基礎的檢視一起使用,以確保只有透過測試的使用者才能存取檢視。

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

def test_func(user):
    # 自訂測試邏輯
    return user.date_joined.year > 2020 or user.username == 'alice'

@user_passes_test(test_func)
def my_view(request):
    # 只有透過測試的使用者才能存取此檢視
    return JsonResponse({'message': 'Hello, world!'})

這些授權機制可以幫助您保護 Django 應用程式中的資源,確保只有具備適當許可的使用者才能存取敏感資料。

10.2.3 條件性顯示

通常不建議顯示使用者無權存取的內容。例如,如果 Bob 沒有刪除其他使用者帳戶的許可權,那麼他不應該看到「刪除使用者」的連結或按鈕,以免造成混淆。這個問題可以透過條件性顯示來解決:根據使用者的許可權,元素可以被隱藏或顯示為停用狀態。 Django 的範本機制已經內建了根據授權的條件性顯示。當前使用者的許可權可以透過 perms 變數存取。在以下範本示例中,展示瞭如何根據使用者是否具有傳送驗證訊息的許可權來顯示連結:

{% if perms.messaging.send_authenticatedmessage %}
<a href='/authenticated_message_form/'>Send Message</a>
{% endif %}

這種方法也可以用於顯示元素為停用狀態。例如,以下程式碼將顯示所有使用者的元素,但只有具有建立新使用者帳戶許可權的使用者才能執行該操作:

<input type='submit'
{% if not perms.auth.add_user %} disabled {% endif %}
value='Add User'/>

10.2.4 授權測試

在第 8 章中,您學習瞭如何測試身份驗證;授權也同樣容易測試。以下程式碼示範瞭如何測試您的系統是否正確限制了對受保護資源的存取:

class TestAuthorization(TestCase):
    def setUp(self):
        passphrase = 'fraying unwary division crevice'
        self.charlie = User.objects.create_user('charlie', password=passphrase)
        self.client.login(username=self.charlie.username, password=passphrase)

    def test_authorize_by_permission(self):
        url = '/messaging/authenticated_message/'
        response = self.client.get(url, secure=True)
        self.assertEqual(403, response.status_code)

        permission = Permission.objects.get(codename='view_authenticatedmessage')
        #...

在這個測試中,首先建立了一個名為 Charlie 的使用者,並登入到系統。然後,測試方法測試 Charlie 是否可以存取受保護的資源(在本例中為 /messaging/authenticated_message/)。如果 Charlie 沒有相應的許可權,則預期狀態碼為 403(Forbidden)。接下來,測試方法授予 Charlie 相應的許可權,並再次測試存取受保護資源的許可權。這次,預期狀態碼為 200(OK),表示 Charlie 現在可以存取該資源。

瞭解授權的重要性

授權是任何安全系統中的關鍵元件,負責定義使用者可以執行哪些操作。它與身份驗證(Authenticaiton)密切相關,但兩者是不同的。身份驗證確定使用者是誰,而授權則確定使用者可以做什麼。

基本概念

  • 使用者(User):系統的使用者。
  • 群組(Group):使用者的集合,通常根據組織角色或職責進行分組。
  • 許可(Permission):定義使用者或群組可以執行的特定操作。

許可的管理

  • 透過群組成員資格授予許可:這種方法可以簡化許可的管理,減少技術上的複雜性。
  • 強制檢查獨立的自主許可:這有助於確保使用者只有必要的許可,同時也能夠控制和管理許可。

最佳實踐

  • 建立有意義的群組:群組應該根據組織中的實際角色或職責。
  • 避免過度複雜:過多的群組和許可可能會增加管理成本和複雜性。
  • 使用高層級API進行授權:這可以簡化開發過程,減少錯誤。

OAuth 2.0簡介

OAuth 2.0是一種廣泛使用的授權框架,允許使用者授予第三方應用程式存取其受保護資源的許可權,而無需分享密碼。它提供了一種安全、靈活的方式來管理不同應用程式之間的授權。

內容解密:

上述內容介紹了授權的基本概念、最佳實踐以及OAuth 2.0的簡介。授權是確定使用者可以執行哪些操作的過程,與身份驗證密切相關但又有所不同。透過群組和許可的管理,可以實作安全、高效的授權系統。OAuth 2.0是一種廣泛使用的授權框架,提供了一種安全、靈活的方式來管理不同應用程式之間的授權。

圖表翻譯:

  flowchart TD
    A[使用者] -->|屬於|> B[群組]
    B -->|具有|> C[許可]
    C -->|定義|> D[操作]
    D -->|受控於|> E[OAuth 2.0]

上述Mermaid圖表展示了使用者、群組、許可、操作以及OAuth 2.0之間的關係。使用者屬於某個群組,群組具有特定的許可,許可定義了可以執行的操作,而這些操作可以受OAuth 2.0框架的控制。

OAuth 2.0 授權協定簡介

OAuth 2.0 是一個業界標準的授權協定,允許使用者授權第三方應用程式存取其受保護的資源,而無需將帳戶密碼等敏感資訊透露給第三方應用程式。這個協定定義了四種不同的授權型別,包括授權碼(Authorization Code)、隱含授權(Implicit)、密碼授權(Resource Owner Password Credentials)和使用者憑證授權(Client Credentials)。

OAuth 2.0 的角色

在 OAuth 2.0 中,定義了四種角色:

  1. 資源伺服器(Resource Server):負責保護受保護的資源。
  2. 授權伺服器(Authorization Server):負責處理使用者的授權請求。
  3. 使用者(Resource Owner):即資源的擁有者,通常是終端使用者。
  4. 客戶端(Client):即第三方應用程式,需要存取受保護的資源。

OAuth 2.0 的工作流程

OAuth 2.0 的工作流程如下:

  1. 客戶端向使用者請求授權。
  2. 使用者授權客戶端存取受保護的資源。
  3. 客戶端向授權伺服器請求授權碼。
  4. 授權伺服器核實使用者的身份並發放授權碼。
  5. 客戶端使用授權碼向資源伺服器請求存取受保護的資源。
  6. 資源伺服器核實授權碼並允許客戶端存取受保護的資源。

授權碼(Authorization Code)型別

授權碼型別是 OAuth 2.0 中最常用的授權型別。客戶端向使用者請求授權,使用者授權後,客戶端會收到一個授權碼,然後客戶端使用這個授權碼向資源伺服器請求存取受保護的資源。

內容解密:

上述工作流程中,每一步驟都涉及到安全性和授權的問題。例如,客戶端如何確保使用者的身份?如何防止授權碼被竊取或濫用?這些問題都需要在實際實作中進行妥善處理。

圖表翻譯:

下圖示範了 OAuth 2.0 的工作流程:

  sequenceDiagram
    participant 使用者 as Resource Owner
    participant 客戶端 as Client
    participant 授權伺服器 as Authorization Server
    participant 資源伺服器 as Resource Server
    
    使用者->>客戶端: 請求授權
    客戶端->>授權伺服器: 請求授權碼
    授權伺服器->>使用者: 核實身份
    使用者->>授權伺服器: 授權
    授權伺服器->>客戶端: 發放授權碼
    客戶端->>資源伺服器: 請求存取受保護的資源
    資源伺服器->>客戶端: 核實授權碼
    資源伺服器->>客戶端: 允許存取受保護的資源

這個圖表展示了 OAuth 2.0 的工作流程,包括使用者授權、客戶端請求授權碼、授權伺服器核實身份、發放授權碼、客戶端請求存取受保護的資源等步驟。

根據提供的指令,以下是重新創作的內容:

OAuth 2.0 授權流程

OAuth 2.0是一種廣泛使用的授權框架,允許使用者授權第三方應用程式存取其資源,而無需將密碼或其他敏感資訊交給第三方應用程式。

授權碼流程

授權碼流程是OAuth 2.0中最常用的授權流程。以下是授權碼流程的步驟:

  1. 使用者存取第三方應用程式:使用者存取第三方應用程式,並要求存取其資源。
  2. 第三方應用程式要求授權:第三方應用程式將使用者重定向到授權伺服器,以要求授權。
  3. 使用者授權:使用者在授權伺服器上授權第三方應用程式存取其資源。
  4. 授權碼傳送:授權伺服器將授權碼傳送給第三方應用程式。
  5. 第三方應用程式換取存取令牌:第三方應用程式使用授權碼換取存取令牌。
  6. 存取受保護資源:第三方應用程式使用存取令牌存取受保護資源。

實作授權碼流程

以下是實作授權碼流程的步驟:

  1. 註冊OAuth 2.0客戶端:第三方應用程式需要註冊OAuth 2.0客戶端,以獲得客戶端ID和客戶端密碼。
  2. 建立授權URL:第三方應用程式需要建立授權URL,以重定向使用者到授權伺服器。
  3. 處理授權碼:第三方應用程式需要處理授權碼,以換取存取令牌。
  4. 使用存取令牌:第三方應用程式需要使用存取令牌,以存取受保護資源。

安全考量

OAuth 2.0授權流程需要考慮安全性,以防止未經授權的存取。以下是安全考量:

  1. 使用HTTPS:OAuth 2.0授權流程需要使用HTTPS,以確保資料傳輸的安全性。
  2. 驗證客戶端:授權伺服器需要驗證客戶端的身份,以確保只有合法的客戶端可以存取資源。
  3. 使用授權碼:授權碼需要使用一次,以防止重複使用。
  4. 限制存取範圍:第三方應用程式需要限制存取範圍,以防止未經授權的存取。

12.2.2 授權流程

授權流程是OAuth 2.0中的一個重要步驟,負責讓使用者授權第三方應用程式存取其資源。以下是授權流程的詳細步驟:

  1. 使用者授權: 使用者存取第三方應用程式,並要求存取其資源。
  2. 授權請求: 第三方應用程式將使用者重定向到授權伺服器的授權頁面。
  3. 使用者授權: 使用者在授權頁面上授權第三方應用程式存取其資源。
  4. 授權碼: 授權伺服器將授權碼傳送給第三方應用程式。
  5. 令牌請求: 第三方應用程式使用授權碼請求令牌。
  6. 令牌傳送: 授權伺服器將令牌傳送給第三方應用程式。

12.2.3 令牌交換

令牌交換是OAuth 2.0中的一個重要步驟,負責讓第三方應用程式交換授權碼為令牌。以下是令牌交換的詳細步驟:

  1. 令牌請求: 第三方應用程式使用授權碼請求令牌。
  2. 令牌傳送: 授權伺服器將令牌傳送給第三方應用程式。
  3. 令牌驗證: 第三方應用程式驗證令牌的有效性。

12.2.4 存取保護資源

存取保護資源是OAuth 2.0中的一個重要步驟,負責讓第三方應用程式存取保護資源。以下是存取保護資源的詳細步驟:

  1. 存取請求: 第三方應用程式使用令牌存取保護資源。
  2. 資源伺服器驗證: 資源伺服器驗證令牌的有效性。
  3. 資源傳送: 資源伺服器將資源傳送給第三方應用程式。

從技術架構視角來看,Django 的授權機制提供了一個兼顧安全性和靈活性的解決方案。透過群組、許可權和使用者之間的多對多關係,可以精細地控制不同使用者對資源的存取。然而,低階 API 的使用存在潛在風險,例如許可權快取更新不及時可能導致錯誤的授權結果。UserPassesTestMixin 和相關裝飾器提供更彈性的自訂測試機制,允許開發者根據業務邏輯實作更複雜的授權策略。

分析 Django 授權機制在實務落地的情況,我們發現直接使用 has_perm() 等低階 API 容易忽略快取問題,可能導致安全漏洞。建議優先使用 PermissionRequiredMixin 或 @permission_required 裝飾器,它們封裝了最佳實踐,並簡化了授權流程。同時,應注意避免過度使用許可權,以降低管理複雜度。對於需要更精細控制的場景,UserPassesTestMixin 和自訂測試函式提供了更強大的能力。

展望未來,隨著應用程式安全需求的日益提升,預計 Django 的授權機制將持續演進,例如更細粒度的許可權控制、更完善的快取機制以及與其他安全框架的整合。此外,OAuth 2.0 等標準協定的整合也將進一步提升 Django 應用程式的安全性與互通性。

玄貓認為,掌握 Django 授權機制的核心概念和最佳實務,並結合實際應用場景進行調整,對於構建安全可靠的 Web 應用程式至關重要。開發者應持續關注 Django 安全方面的更新,並積極探索新的授權策略,以應對不斷變化的安全挑戰。