Djangoauthentication and authorization

LearningDjango userauthentication, register, login, logout and permissionmanagement

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

  1. creationuserregister表单, package含user名, 邮箱 and password字段.
  2. implementationregister视graph and 模板.
  3. configurationlogin and logout视graph.
  4. creationlogin and logout模板.
  5. testregister, login and logoutfunctions.

练习 2: implementationpasswordresetfunctions

  1. configurationURLrouting, 添encryption码reset相关视graph.
  2. creationpasswordreset相关模板.
  3. configurationemailserver (using控制台 after 端) .
  4. testpasswordresetfunctions.

练习 3: creation自定义usermodel

  1. creation自定义usermodel, 添加bio and avatar字段.
  2. configuration自定义usermodel.
  3. creation自定义user表单 and management界面.
  4. 执行migration.
  5. test自定义usermodelfunctions.