Djangoauthenticationsystemoverview
Djangoproviding了一个完整 authenticationsystem, includinguserauthentication, permissionmanagement, 组managementetc.functions. 它 is Djangoframework corecomponent之一, for Webapplicationproviding了security reliable usermanagementfunctions.
Djangoauthenticationsystem主要including以 under component:
- usermodel (User Model)
- authentication after 端 (Authentication Backends)
- permissionsystem (Permissions System)
- 组system (Groups System)
- login and logout视graph
- passwordreset and modifyfunctions
提示
Djangoauthenticationsystem默认usingSessionauthentication, throughCookiestoreSession ID, 适合传统 Webapplication. for 于 before after 端分离 application, 可以usingTokenauthentication or JWTauthentication.
in 置usermodel
Djangoproviding了一个 in 置 Usermodel, 位于django.contrib.auth.modelsmodulein. in 置Usermodelpackage含以 under 字段:
- username: user名, 必填, 最 long 255个字符.
- first_name: 名, 可选, 最 long 30个字符.
- last_name: 姓, 可选, 最 long 30个字符.
- email: 邮箱, 可选.
- password: password, 必填, store is 哈希值.
- is_staff: is 否 for management员, 可以loginmanagement界面.
- is_active: is 否激活, 默认 for True.
- is_superuser: is 否 for 超级user, 拥 has 所 has permission.
- last_login: 最 after login时间.
- date_joined: 账号creation时间.
userregister
可以usingDjango in 置表单 or 自定义表单来implementationuserregisterfunctions.
using in 置表单register
# blog/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm
from django.contrib.auth.models import User
class RegisterForm(UserCreationForm):
email = forms.EmailField(required=True)
class Meta:
model = User
fields = ['username', 'email', 'password1', 'password2']
register视graph
# blog/views.py
from django.shortcuts import render, redirect
from django.contrib import messages
from .forms import RegisterForm
def register(request):
if request.method == 'POST':
form = RegisterForm(request.POST)
if form.is_valid():
form.save()
username = form.cleaned_data.get('username')
messages.success(request, f'账号 {username} creation成功!')
return redirect('login')
else:
form = RegisterForm()
return render(request, 'blog/register.html', {'form': form})
register模板
<!-- blog/templates/blog/register.html -->
{% extends 'blog/base.html' %}
{% block title %}register{% endblock %}
{% block content %}
<h1>register new 账号</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">register</button>
</form>
{% endblock %}
URLconfiguration
# blog/urls.py
from django.urls import path
from . import views
urlpatterns = [
# otherURLconfiguration...
path('register/', views.register, name='register'),
]
userlogin and logout
Djangoproviding了 in 置 login and logout视graph, 可以直接using.
URLconfiguration
# blog/urls.py
from django.urls import path, include
from django.contrib.auth import views as auth_views
from . import views
urlpatterns = [
# otherURLconfiguration...
# login视graph
path('login/', auth_views.LoginView.as_view(template_name='blog/login.html'), name='login'),
# logout视graph
path('logout/', auth_views.LogoutView.as_view(template_name='blog/logout.html'), name='logout'),
]
login模板
<!-- blog/templates/blog/login.html -->
{% extends 'blog/base.html' %}
{% block title %}login{% endblock %}
{% block content %}
<h1>login</h1>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">login</button>
</form>
<p>还没 has 账号?<a href="{% url 'register' %}">register</a></p>
{% endblock %}
logout模板
<!-- blog/templates/blog/logout.html -->
{% extends 'blog/base.html' %}
{% block title %}logout{% endblock %}
{% block content %}
<h1>已成功logout</h1>
<p>您已成功退出login. </p>
<a href="{% url 'login' %}">重 new login</a>
{% endblock %}
passwordreset
Djangoproviding了完整 passwordresetfunctions, includingrequestreset, 发送email, 设置 new passwordetc.步骤.
URLconfiguration
# blog/urls.py
from django.urls import path, include
from django.contrib.auth import views as auth_views
urlpatterns = [
# otherURLconfiguration...
# passwordresetrequest
path('password-reset/',
auth_views.PasswordResetView.as_view(template_name='blog/password_reset.html'),
name='password_reset'),
# passwordresetemail发送成功
path('password-reset/done/',
auth_views.PasswordResetDoneView.as_view(template_name='blog/password_reset_done.html'),
name='password_reset_done'),
# passwordreset链接
path('password-reset-confirm///',
auth_views.PasswordResetConfirmView.as_view(template_name='blog/password_reset_confirm.html'),
name='password_reset_confirm'),
# passwordreset成功
path('password-reset-complete/',
auth_views.PasswordResetcompleteView.as_view(template_name='blog/password_reset_complete.html'),
name='password_reset_complete'),
]
configurationemailserver
要usingpasswordresetfunctions, 需要 in settings.pyinconfigurationemailserver:
# settings.py
# emailserverconfiguration
EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.example.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = 'your_email@example.com'
EMAIL_HOST_PASSWORD = 'your_password'
DEFAULT_FROM_EMAIL = 'your_email@example.com'
for 于Developmentenvironment, 可以usingDjango 控制台 after 端, 将email输出 to 控制台:
# settings.py
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
保护视graph
可以using装饰器 or class视graph混入来保护需要login才能访问 视graph.
using装饰器保护function视graph
# blog/views.py
from django.contrib.auth.decorators import login_required
@login_required
def profile(request):
return render(request, 'blog/profile.html')
using混入保护class视graph
# blog/views.py
from django.contrib.auth.mixins import LoginRequiredMixin
from django.views.generic import TemplateView
class ProfileView(LoginRequiredMixin, TemplateView):
template_name = 'blog/profile.html'
# loginURL, 默认 for settings.LOGIN_URL
login_url = '/login/'
# login after 重定向 URL名称
redirect_field_name = 'next'
permissionmanagement
Django permissionsystem允许management员 for user or 组分配specific permission, 控制user可以访问哪些functions.
checkuserpermission
可以using装饰器 or 手动check来verificationuser is 否具 has specificpermission.
using装饰器checkpermission
# blog/views.py
from django.contrib.auth.decorators import permission_required
# check单个permission
@permission_required('blog.view_blogpost')
def post_list(request):
# 视graph逻辑...
# check many 个permission, using逻辑 or
@permission_required(('blog.view_blogpost', 'blog.change_blogpost'))
def post_detail(request, pk):
# 视graph逻辑...
# check many 个permission, using逻辑 and
@permission_required('blog.view_blogpost', raise_exception=True)
@permission_required('blog.change_blogpost', raise_exception=True)
def post_edit(request, pk):
# 视graph逻辑...
手动checkpermission
# blog/views.py
def post_edit(request, pk):
if request.user.has_perm('blog.change_blogpost'):
# user has 编辑permission
# 视graph逻辑...
else:
# user没 has 编辑permission
return HttpResponseForbidden()
# checkuser is 否属于某个组
if request.user.groups.filter(name='Editors').exists():
# user属于Editors组
# 视graph逻辑...
in 模板incheckpermission
<!-- checkuser is 否login -->
{% if user.is_authenticated %}
<p>欢迎, {{ user.username }}!</p>
<a href="{% url 'logout' %}">logout</a>
{% else %}
<a href="{% url 'login' %}">login</a>
<a href="{% url 'register' %}">register</a>
{% endif %}
<!-- checkuser is 否 has specificpermission -->
{% if perms.blog.change_blogpost %}
<a href="{% url 'post_edit' post.pk %}">编辑文章</a>
{% endif %}
<!-- checkuser is 否属于某个组 -->
{% if user.groups.all.0.name == 'Editors' %}
<p>您 is 编辑, 可以编辑所 has 文章. </p>
{% endif %}
自定义usermodel
such as果 in 置 Usermodel不符合requirements, 可以自定义usermodel. 建议 in project开始时就考虑 is 否需要自定义usermodel, 因 for after 续modify会比较麻烦.
creation自定义usermodel
# blog/models.py
from django.contrib.auth.models import AbstractUser
from django.db import models
class CustomUser(AbstractUser):
# 添加自定义字段
bio = models.TextField(blank=True)
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
phone_number = models.CharField(max_length=15, blank=True)
# 可以添加自定义method
def __str__(self):
return self.username
configuration自定义usermodel
需要 in settings.pyinconfiguration自定义usermodel:
# settings.py
AUTH_USER_MODEL = 'blog.CustomUser'
creation自定义user表单
# blog/forms.py
from django import forms
from django.contrib.auth.forms import UserCreationForm, UserChangeForm
from .models import CustomUser
class CustomUserCreationForm(UserCreationForm):
class Meta:
model = CustomUser
fields = ('username', 'email', 'bio', 'phone_number')
class CustomUserChangeForm(UserChangeForm):
class Meta:
model = CustomUser
fields = ('username', 'email', 'bio', 'avatar', 'phone_number')
in management界面register自定义usermodel
# blog/admin.py
from django.contrib import admin
from django.contrib.auth.admin import UserAdmin
from .models import CustomUser
from .forms import CustomUserCreationForm, CustomUserChangeForm
class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = ['username', 'email', 'is_staff', 'is_active']
# 可以自定义othermanagement界面选项
admin.site.register(CustomUser, CustomUserAdmin)
warning
自定义usermodel after , 需要 in 执行第一次migration之 before configuration good . such as果已经执行了migration, modifyusermodel会比较 complex , 可能需要重 new creationdatalibrary or writing自定义migration.
authentication after 端
Django authentication after 端用于verificationuser 凭据并返回userobject. Django默认usingModelBackend, 它usingdatalibraryin usermodelforauthentication.
自定义authentication after 端
可以creation自定义authentication after 端, implementation例such asusing邮箱login, LDAPauthenticationetc.functions.
# blog/backends.py
from django.contrib.auth.backends import ModelBackend
from django.contrib.auth import get_user_model
from django.db.models import Q
class EmailBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
UserModel = get_user_model()
try:
# 尝试usinguser名 or 邮箱login
user = UserModel.objects.get(Q(username=username) | Q(email=username))
except UserModel.DoesNotExist:
return None
else:
# verificationpassword
if user.check_password(password):
return user
return None
def get_user(self, user_id):
UserModel = get_user_model()
try:
return UserModel.objects.get(pk=user_id)
except UserModel.DoesNotExist:
return None
然 after in settings.pyinconfigurationauthentication after 端:
# settings.py
AUTHENTICATION_BACKENDS = [
'blog.backends.EmailBackend', # 自定义authentication after 端
'django.contrib.auth.backends.ModelBackend', # 默认authentication after 端
]
user资料页面
可以creationuser资料页面, 允许user查看 and 编辑自己 个人information.
# blog/forms.py
from django import forms
from .models import CustomUser
class ProfileForm(forms.ModelForm):
class Meta:
model = CustomUser
fields = ('first_name', 'last_name', 'email', 'bio', 'avatar', 'phone_number')
widgets = {
'bio': forms.Textarea(attrs={'rows': 4}),
}
# blog/views.py
from django.shortcuts import render, redirect
from .forms import ProfileForm
@login_required
def profile(request):
if request.method == 'POST':
form = ProfileForm(request.POST, request.FILES, instance=request.user)
if form.is_valid():
form.save()
messages.success(request, '资料update成功!')
return redirect('profile')
else:
form = ProfileForm(instance=request.user)
return render(request, 'blog/profile.html', {'form': form})
# blog/urls.py
urlpatterns = [
# otherURLconfiguration...
path('profile/', views.profile, name='profile'),
]
练习 1: implementationuserregister and loginfunctions
- creationuserregister表单, package含user名, 邮箱 and password字段.
- implementationregister视graph and 模板.
- configurationlogin and logout视graph.
- creationlogin and logout模板.
- testregister, login and logoutfunctions.
练习 2: implementationpasswordresetfunctions
- configurationURLrouting, 添encryption码reset相关视graph.
- creationpasswordreset相关模板.
- configurationemailserver (using控制台 after 端) .
- testpasswordresetfunctions.
练习 3: creation自定义usermodel
- creation自定义usermodel, 添加bio and avatar字段.
- configuration自定义usermodel.
- creation自定义user表单 and management界面.
- 执行migration.
- test自定义usermodelfunctions.