title: 深入Django:使用者認證與許可權控制實戰指南
date: 2024/5/7 18:50:33
updated: 2024/5/7 18:50:33
categories:
- 後端開發
tags:
- Auth
- Decorators
- Permissions
- Guardian
- RESTAuth
- SessionMgmt
- MFA
第1章:入門Django與設定
1.1 Django安裝與環境配置
在開始使用Django之前,需要確保已經安裝了Python環境。在安裝好Python後,可以使用以下命令安裝Django:
pip install Django
確保已安裝Django後,可以使用以下命令檢查版本:
django-admin --version
1.2 建立第一個Django專案和應用
建立一個新的Django專案:
django-admin startproject myproject
進入專案目錄:
cd myproject
建立一個新的Django應用:
python manage.py startapp myapp
1.3 設定基礎使用者模型
Django預設提供一個基本的使用者模型,如果需要定製使用者模型,可以在myproject/settings.py
中進行設定。
例如,可以新增一個新的欄位age
:
# myproject/settings.py
AUTH_USER_MODEL = 'myapp.NewUser'
# myapp/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class NewUser(AbstractUser):
age = models.PositiveIntegerField(null=True, blank=True)
def __str__(self):
return f'{self.username} ({self.email})'
在資料庫遷移中應用新的使用者模型:
python manage.py makemigrations
python manage.py migrate
現在,已經完成了基本的Django專案和應用的建立,並設定了一個基礎的使用者模型。
第2章:Django認證系統
2.1 使用者模型詳解
在Django中,AUTH_USER_MODEL
設定決定了你如何處理使用者。預設的使用者模型是django.contrib.auth.models.User
,但如你之前設定的,可以建立自定義使用者模型。例如,NewUser
模型需要繼承AbstractUser
,幷包含username
,email
, 和age
等欄位。
2.2 使用者註冊、登入與驗證
- 註冊:Django的
UserCreationForm
和UserChangeForm
可以用於使用者註冊,通常在myapp/forms.py
中定義。在views.py
中使用UserCreationForm.as_view()
建立檢視。
from django.contrib.auth import get_user_model
from django.urls import reverse_lazy
from django.views import generic
class RegisterView(generic.CreateView):
form_class = get_user_model().forms.UserCreationForm
success_url = reverse_lazy('login')
template_name = 'registration/register.html'
- 登入:
LoginView
和LogoutView
在django.contrib.auth.views
中,使用LoginView.as_view()
和LogoutView.as_view()
。
from django.contrib.auth.views import LoginView, LogoutView
class CustomLoginView(LoginView):
template_name = 'registration/login.html'
- 驗證:使用者登入後,Django會自動驗證使用者。在
views.py
中,你可以使用@login_required
裝飾器確保只有登入使用者才能訪問某些檢視。
from django.contrib.auth.decorators import login_required
@login_required
def protected_view(request):
# 只有已登入使用者可以訪問的檢視
...
2.3 自定義認證檢視和URL
要自定義登入或註冊檢視,可以建立一個類檢視並定義get
和post
方法。在urls.py
中,使用path
來對映URL到這些檢視。
from django.urls import path
from .views import CustomLoginView, CustomRegisterView
urlpatterns = [
path('login/', CustomLoginView.as_view(), name='login'),
path('register/', CustomRegisterView.as_view(), name='register'),
]
2.4 CSRF保護
Django的CSRF(跨站請求偽造)保護是透過在表單中新增一個隱藏的CSRF
token來實現的。確保在所有需要使用者互動的表單(如註冊和登入表單)中使用{% csrf_token %}
模板標籤。
此外,你可以在settings.py
中啟用CSRF保護:
CSRF_COOKIE_NAME = 'my_custom_cookie_name'
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
這將確保CSRF令牌的安全傳輸。
第3章:認證中介軟體和訊號
3.1 中介軟體在認證中的作用
認證中介軟體
AD:首頁 | 一個覆蓋廣泛主題工具的高效線上平臺
是Django提供的一種處理請求和響應的機制,它位於檢視函式之前和之後。在認證中,django.contrib.auth.middleware.AuthenticationMiddleware
負責檢測請求是否已透過身份驗證。如果使用者未登入,中介軟體會重定向到登入頁面。一旦使用者登入,中介軟體會設定request.user
,使得檢視可以訪問使用者資訊。
3.2 使用者登入狀態管理
Django的認證系統會在使用者登入後自動設定request.user
,這使得在檢視中可以透過request.user
訪問使用者資訊。但如果你想在會話之外儲存使用者狀態(例如,記住使用者名稱),可以使用
AD:專業搜尋引擎
django.contrib.auth.middleware.SessionAuthenticationMiddleware
,它會將使用者資訊儲存在會話中。如果需要自定義登入狀態的永續性,可以考慮使用django.contrib.auth.backends.RemoteUserBackend
或建立自己的Backend
實現。
3.3 認證訊號與事件處理
認證訊號是Django的一種通知機制,用於在認證過程中觸發特定事件。例如,user_logged_in
和user_logged_out
訊號分別在使用者登入和登出時傳送。你可以透過註冊接收器來處理這些訊號,執行額外的操作,如傳送郵件通知或更新使用者會話記錄。
from django.contrib.auth import get_user_model
from django.dispatch import receiver
from django.db.models.signals import post_save
# 註冊接收器
@receiver(post_save, sender=get_user_model())
def create_or_update_user_profile(sender, instance, created, **kwargs):
if created:
# 使用者建立時,建立使用者profile
else:
# 使用者更新時,更新userprofile
透過這種方式,你可以擴充套件Django認證系統的功能,而無需直接修改核心程式碼。
第4章:許可權系統基礎
4.1 Django內建許可權模型
Django的許可權系統主要依賴於django.contrib.auth.models
模組中的以下幾個模型:
User
:表示使用者,每個使用者都有一個唯一的使用者名稱和密碼。Group
:用於組織使用者,一個使用者可以屬於多個組。Permission
:表示一個操作或資源的許可權,例如“管理部落格”或“檢視文章”。UserPermission
(也稱為user_has_perm
):關聯使用者和許可權,表示使用者擁有特定的許可權。
4.2 使用者角色與許可權關聯
在Django中,使用者和許可權通常是透過Group
來關聯的。使用者可以被新增到一個或多個組中,而組則被賦予了特定的許可權。這樣,如果一個使用者屬於某個具有特定許可權的組,那麼該使用者就自動獲得了該組的所有許可權。透過這種方式,可以實現角色(如管理員、編輯員等)的許可權管理。
4.3 分組與許可權管理
- 建立和管理組:使用
Group
模型,你可以建立新的組,然後將使用者新增到組中。例如:
group = Group.objects.create(name='Administrators')
user = User.objects.get(username='john')
group.user_set.add(user)
- 為組分配許可權:使用
Permission
模型,為組賦予特定的許可權,然後將組與這些許可權關聯起來。例如:
permission = Permission.objects.get(codename='add_article')
group.permissions.add(permission)
- 檢查使用者許可權:在檢視中,可以使用
user.has_perm()
或user.has_module_perms()
方法檢查使用者是否具有特定的許可權。
if request.user.has_perm('blog.add_article'):
# 使用者有新增文章的許可權
透過這種方式,Django的許可權系統允許你以靈活的方式管理使用者對應用的不同部分的訪問。
第5章:自定義許可權和驗證
5.1 使用@permission_required裝飾器
Django提供了一個名為@permission_required
的裝飾器,用於在檢視函式中檢查使用者是否具有特定許可權。例如:
from django.contrib.auth.decorators import permission_required
def some_view(request):
@permission_required('blog.add_article')
def inner_view(request):
# 檢視程式碼,如果使用者沒有新增文章的許可權,將被重定向到登入頁面
...
return inner_view(request)
如果使用者沒有blog.add_article
許可權,請求會重定向到登入頁面,或者根據設定顯示一個403 Forbidden錯誤。
5.2 編寫自定義許可權類
Django允許你自定義許可權類來實現更復雜的許可權檢查。建立一個繼承自BasePermission
的類,然後在has_permission
方法中定義你的邏輯:
from django.contrib.auth.models import Permission
from django.contrib.auth.base_permissions import BasePermission
class IsSuperAdmin(BasePermission):
def has_permission(self, request, view):
# 檢查使用者是否是超級管理員
return request.user.is_superuser
然後在檢視中使用這個自定義許可權:
def some_view(request):
permission_classes = (IsSuperAdmin,)
# 使用自定義許可權檢查
if not request.user.has_perm(IsSuperAdmin()):
return HttpResponseForbidden('You are not authorized.')
...
5.3 許可權驗證最佳實踐
- 避免硬編碼:不要在程式碼中硬編碼使用者的許可權檢查,而應使用Django的許可權系統。
- 使用裝飾器:
@permission_required
裝飾器是快速新增許可權檢查的好工具,但也要注意不要過度使用,可能導致程式碼難以理解和維護。 - 許可權分離:將許可權管理與業務邏輯分離,確保許可權檢查獨立於具體的檢視。
- 使用許可權類:自定義許可權類可以提供更靈活的許可權控制,例如基於角色、使用者組或使用者屬性的許可權。
- 錯誤處理:對於許可權檢查失敗的情況,提供清晰的錯誤資訊和適當的使用者反饋,如重定向到登入頁面或顯示一個友好的錯誤訊息。
AD:漫畫首頁
遵循這些最佳實踐,可以確保你的許可權系統既安全又易於管理。
第6章:許可權管理框架(如django-guardian)
6.1 django-guardian的介紹
django-guardian是一個Django應用,用於在Django專案中實現更細粒度的許可權控制。django-guardian允許你將許可權授予單個物件,而不僅僅是對整個模型的訪問許可權。
6.2 細化許可權控制
django-guardian為你提供了一種在Django中實現更細粒度的許可權控制的方法。django-guardian的核心是ObjectPermission
模型,用於將許可權與單個物件關聯。
django-guardian允許你:
- 為單個物件設定和管理許可權。
- 對物件進行精細的許可權控制。
- 在檢視中進行簡單的許可權檢查。
6.3 例項演示
首先,安裝django-guardian:
pip install django-guardian
在你的Django專案中,新增'guardian'
到INSTALLED_APPS
中,並在MIDDLEWARE
中新增'guardian.middleware.UpdatePermissionsMiddleware'
。
接下來,讓我們建立一個簡單的應用,並在其中使用django-guardian:
-
建立一個名為
articles
的應用:python manage.py startapp articles
-
在
articles
應用中,建立一個名為models.py
的檔案,並新增以下程式碼:from django.db import models from django.contrib.auth.models import User from guardian.shortcuts import assign_perm, get_objects_for_user class Article(models.Model): title = models.CharField(max_length=100) content = models.TextField() author = models.ForeignKey(User, on_delete=models.CASCADE) def __str__(self): return self.title
-
在
articles
應用中,建立一個名為views.py
的檔案,並新增以下程式碼:from django.shortcuts import render, get_object_or_404 from django.contrib.auth.decorators import login_required from django.http import HttpResponseForbidden from articles.models import Article, assign_perm, get_objects_for_user @login_required def article_list(request): user = request.user articles = get_objects_for_user(user, 'articles.view_article', klass=Article) return render(request, 'articles/article_list.html', {'articles': articles}) @login_required def assign_article_permissions(request, article_id): user = request.user article = get_object_or_404(Article, id=article_id) if request.method == 'POST': if user.has_perm('articles.change_article', article): assign_perm('articles.view_article', user, article) return HttpResponse('Assigned view permission.') else: return HttpResponseForbidden('You do not have the permission to change article.') return render(request, 'articles/assign_permissions.html', {'article': article})
-
在
articles
應用中,建立一個名為templates/articles
的資料夾,並在其中建立一個名為assign_permissions.html
的檔案:<form method="post"> {% csrf_token %} <input type="submit" value="Assign Permission"> </form>
-
在
articles
應用中,建立一個名為urls.py
的檔案,並新增以下程式碼:from django.urls import path from . import views urlpatterns = [ path('', views.article_list, name='article_list'), path('assign_permissions/<int:article_id>/', views.assign_article_permissions, name='assign_permissions'), ]
-
在你的專案中,將
articles
應用的urls.py
新增到主urls.py
中:from django.contrib import admin from django.urls import path, include urlpatterns = [ path('admin/', admin.site.urls), path('articles/', include('articles.urls')), ]
現在,你可以執行你的專案,並在文章列表中檢視許可權管理的例項。在這個例子中,我們使用django-guardian為每個使用者分配了對文章的特定檢視許可權。
你可以使用django-guardian的assign_perm
和get_objects_for_user
函式來分配和檢查許可權。在這個例子中,我們使用了一個名為view_article
的許可權,可以在admin.py
中進行定義:
from django.contrib import admin
from django.contrib.auth.models import Permission
from articles.models import Article
class ArticleAdmin(admin.ModelAdmin):
list_display = ('title', 'author')
def save_model(self, request, obj, form, change):
if not obj.pk:
assign_perm('articles.view_article', request.user, obj)
obj.save()
admin.site.register(Article, ArticleAdmin)
這樣,當你在Django管理介面中建立一個新文章時,將自動為當前使用者分配view_article
許可權。
django-guardian為你提供了一種更細粒度的許可權控制方法,可以在Django專案中實現更強大的許可權管理。
第7章:檢視和模板的許可權控制
7.1 在檢視中檢查許可權
在Django中,檢視是處理HTTP請求的函式或類。為了在檢視中檢查使用者的許可權,可以使用django-guardian
提供的has_perm
和get_objects_for_user
函式。以下是一個基本的檢視檢查許可權的例子:
from django.shortcuts import render, redirect
from django.contrib.auth.decorators import login_required
from guardian.shortcuts import has_perm
@login_required
def some_view(request):
user = request.user
article = Article.objects.get(pk=1) # 假設你有一個名為Article的模型
# 檢查使用者是否有檢視文章的許可權
if not has_perm('articles.view_article', article, user):
return HttpResponseForbidden("You don't have permission to view this article.")
# 如果使用者有許可權,繼續執行
# ...
return render(request, 'some_template.html', {'article': article})
在這個例子中,has_perm
函式檢查使用者是否有指定的許可權(在這個例子中是articles.view_article
)對給定的物件(article
)。
7.2 在模板中顯示許可權相關資訊
在模板中,你可能想要顯示使用者是否具有特定的許可權。Django模板語言允許你使用條件語句來檢查這些許可權。以下是一個簡單的例子:
{% if user.has_perm('articles.view_article', article) %}
<p>You have permission to view this article.</p>
{% else %}
<p>You do not have permission to view this article.</p>
{% endif %}
在這個模板片段中,如果使用者具有articles.view_article
許可權,has_perm
表示式會返回True
,否則返回False
,從而決定顯示不同的內容。
你也可以在模板中使用get_objects_for_user
來獲取使用者具有特定許可權的物件,並根據這些物件的內容進行渲染:
{% if object_list %}
<ul>
{% for object in object_list %}
{% if user.has_perm('articles.view', object) %}
<li>{{ object.title }} (You can view)</li>
{% else %}
<li>{{ object.title }} (You cannot view)</li>
{% endif %}
{% endfor %}
</ul>
{% else %}
<p>No objects found.</p>
{% endif %}
這樣,模板會根據使用者的許可權動態地顯示內容,提供一個使用者友好的介面。
第8章:RESTful API的認證與許可權
8.1 Django REST framework的認證整合
Django REST
framework提供了多種認證方式,包括基本認證(BasicAuthentication)、令牌認證(TokenAuthentication)和會話認證(SessionAuthentication)等。可以在專案的設定檔案中進行配置。以下是一個基本的認證配置示例:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.BasicAuthentication',
'rest_framework.authentication.SessionAuthentication',
)
}
在這個配置中,REST framework將使用基本認證和會話認證來處理API請求。
8.2 API許可權控制策略
API許可權控制是確保API的安全性和保護資料的重要部分。Django REST
framework提供了多種許可權控制策略,包括只讀許可權(ReadOnly)、物件級許可權(ObjectPermissions)和自定義許可權類(CustomPermission)等。可以在檢視中配置許可權控制策略。以下是一個使用物件級許可權的檢視示例:
from rest_framework import viewsets, permissions
from myapp.models import Article
from myapp.serializers import ArticleSerializer
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [permissions.DjangoModelPermissions]
在這個檢視中,我們使用了DjangoModelPermissions,這意味著使用者只能訪問他們擁有的物件。如果使用者試圖訪問他們不擁有的物件,REST
framework將返回一個403 Forbidden響應。
如果你需要更細粒度的許可權控制,可以使用自定義許可權類。以下是一個簡單的自定義許可權類示例:
from rest_framework import permissions
class CustomPermission(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.user.is_staff:
return True
return False
class ArticleViewSet(viewsets.ModelViewSet):
queryset = Article.objects.all()
serializer_class = ArticleSerializer
permission_classes = [CustomPermission]
在這個示例中,我們建立了一個名為CustomPermission的自定義許可權類,它只允許管理員使用者訪問API。如果使用者不是管理員,則返回一個403
Forbidden響應。
總之,Django REST framework為RESTful API的認證和許可權控制提供了多種方式和工具,可以根據專案需求進行選擇和定製。
第9章:高階主題
9.1 使用者會話管理
在Web應用程式中,使用者會話管理是非常重要的,它涉及到使用者登入、登出和會話狀態的管理。Django提供了內建的會話管理功能,而Django
REST framework也提供了相應的支援。透過使用SessionAuthentication認證類,Django REST framework可以與Django的會話管理系統整合,從而實現使用者會話管理。
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.SessionAuthentication',
)
}
透過將SessionAuthentication新增到DEFAULT_AUTHENTICATION_CLASSES中,可以啟用基於會話的認證,從而實現使用者會話管理。
9.2 客戶端認證(OAuth2、JWT等)
客戶端認證是指在API請求中對客戶端進行身份驗證的過程。Django REST framework支援多種客戶端認證方式,包括OAuth2和JWT(JSON Web
Token)等。這些認證方式可以透過第三方庫來實現,比如djangorestframework-simplejwt
用於JWT認證。
以下是一個簡單的JWT認證配置示例:
# settings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework_simplejwt.authentication.JWTAuthentication',
)
}
透過將JWTAuthentication新增到DEFAULT_AUTHENTICATION_CLASSES中,可以啟用JWT認證,從而實現客戶端認證。
9.3 多因素認證(MFA)
多因素認證是指在使用者登入或進行敏感操作時,除了使用者名稱和密碼外,還需要提供第二種或多種身份驗證因素的過程。Django REST
framework本身並沒有內建的多因素認證功能,但可以透過結合第三方庫來實現。例如,可以使用django-mfa
等庫來實現多因素認證功能。
透過使用這些第三方庫,可以實現諸如簡訊驗證碼、一次性密碼(OTP)等多因素認證方式,從而提高系統的安全性。
綜上所述,Django REST framework提供了豐富的支援,可以輕鬆實現使用者會話管理、客戶端認證和多因素認證等高階主題。開發人員可以根據專案需求選擇合適的認證方式,並結合相關的第三方庫來實現所需的功能。
第10章:實戰專案
建立一個簡單的部落格系統是一個很好的實戰專案,可以涵蓋認證與許可權控制等方面。下面是一個基本的Django REST
framework部落格系統的示例,其中包括了認證與許可權控制的應用。
首先,我們需要建立一個Django應用,並定義部落格文章的模型、序列化器、檢視和URL配置。
# models.py
from django.db import models
class Post(models.Model):
title = models.CharField(max_length=100)
content = models.TextField()
author = models.ForeignKey('auth.User', on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
# serializers.py
from rest_framework import serializers
from .models import Post
class PostSerializer(serializers.ModelSerializer):
class Meta:
model = Post
fields = ['id', 'title', 'content', 'author', 'created_at']
# views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
# urls.py
from django.urls import path, include
from rest_framework.routers import DefaultRouter
from .views import PostViewSet
router = DefaultRouter()
router.register(r'posts', PostViewSet)
urlpatterns = [
path('', include(router.urls)),
]
接下來,我們將新增認證與許可權控制。
# settings.py
INSTALLED_APPS = [
# ...
'rest_framework',
'blog', # assuming 'blog' is the name of the app
]
REST_FRAMEWORK = {
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.IsAuthenticated',
],
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication',
'rest_framework.authentication.TokenAuthentication',
],
}
在上述程式碼中,我們將預設許可權設定為IsAuthenticated,這意味著只有經過身份驗證的使用者才能訪問API。我們還配置了SessionAuthentication和TokenAuthentication作為預設的認證類,這樣使用者可以使用會話驗證或令牌驗證進行身份驗證。
最後,我們需要為使用者建立部落格文章的許可權。我們可以使用Django的自定義許可權類來實現這一點。
# permissions.py
from rest_framework import permissions
class IsAuthorOrReadOnly(permissions.BasePermission):
def has_object_permission(self, request, view, obj):
if request.method in permissions.SAFE_METHODS:
return True
return obj.author == request.user
然後,將這個許可權類應用到我們的檢視中。
# views.py
from rest_framework import viewsets
from .models import Post
from .serializers import PostSerializer
from .permissions import IsAuthorOrReadOnly
class PostViewSet(viewsets.ModelViewSet):
queryset = Post.objects.all()
serializer_class = PostSerializer
permission_classes = [IsAuthorOrReadOnly]
現在,我們已經完成了一個簡單的部落格系統,並應用了認證與許可權控制。使用者需要經過身份驗證才能訪問API,並且只有文章的作者才有許可權對文章進行修改或刪除。
第11章:最佳實踐與高階技巧
效能最佳化
- 資料庫最佳化:使用索引、合理設計資料庫結構、避免N+1查詢等。
- 快取:使用快取技術(如Redis、Memcached)快取頻繁訪問的資料。
- 非同步任務:將耗時任務非同步化,可以使用Celery等工具。
- 程式碼最佳化:避免過多的資料庫查詢、減少不必要的計算、最佳化演算法等。
安全注意事項
- 資料加密:對敏感資料進行加密儲存。
- 防止跨站指令碼攻擊(XSS) :對使用者輸入進行過濾和轉義。
- 防止SQL隱碼攻擊:使用ORM或引數化查詢來防止SQL隱碼攻擊。
- 許可權控制:確保使用者只能訪問其有許可權的資源。
- 保持系統更新:定期更新框架、庫和作業系統,以修復潛在的安全漏洞。
未來趨勢與更新
- 微服務架構:將應用拆分為小型的、獨立部署的服務。
- 容器化:使用Docker等容器技術實現應用的快速部署和擴充套件。
- Serverless架構:無需管理伺服器,按需執行程式碼,降低運維成本。
- 人工智慧與機器學習:將人工智慧技術應用於應用開發中,實現智慧化功能。
- 持續整合與持續部署:採用CI/CD流程實現快速迭代和交付。
附錄
Django官方文件連結
你可以在Django官方文件中找到最新的Django文件,包括教程、API參考和釋出說明等。
常見問題解答
- Stack Overflow:在Stack Overflow上搜尋相關問題,通常能找到解決方案。
- Django官方論壇:官方網站上有一個社群論壇,你可以在這裡提問和尋求幫助。
- GitHub Issues:如果你使用的是開源的Django專案或庫,可以在對應的GitHub倉庫的Issues中查詢或提問問題。
工具和庫推薦
- Django REST framework:用於構建Web API的強大工具。
- Celery:用於處理非同步任務和定時任務的分散式任務佇列。
- Django Debug Toolbar:用於在開發階段進行效能分析和除錯的工具。
- Django Channels:為Django引入了實時的、非同步的通訊協議,用於構建實時應用。
- Django Allauth:提供了完整的使用者認證和賬戶管理功能,包括社交賬號登入、郵箱驗證等。
這些工具和庫都是Django社群廣泛使用和推薦的,可以幫助你更高效地開發和管理Django應用。