Django默认admin新增登录验证码

Django默认登录只有用户名密码,存在一定安全隐患,想着提高下,用 django-simple-captcha 实现。

1. 安装 django-simple-captcha

# 如无 pillow,需安装才可正常使用
# pip install pillow
pip install django-simple-captcha

2. 添加应用及配置

# settings
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    # 添加应用
    'captcha',
]

# 验证码图片宽度/高度
CAPTCHA_IMAGE_SIZE = (100, 40)
# 验证码字符长度(数学公式时不生效)
CAPTCHA_LENGTH = 4

# 验证码字符类型:字母数字混合(可选值:'numeric'纯数字 / 'alphabetic'纯字母 / 'alphanumeric'混合)
CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.random_char_challenge'
# 数学公式验证码(替代上述字符验证码)
# CAPTCHA_CHALLENGE_FUNCT = 'captcha.helpers.math_challenge'

# 验证码过期时间(秒)
CAPTCHA_TIMEOUT = 5 * 60  # 5分钟
# 干扰线数量(0=无干扰线,数值越大干扰越强)
CAPTCHA_NOISE_FUNCTIONS = (
    'captcha.helpers.noise_arcs',  # 弧形干扰线
    'captcha.helpers.noise_dots',   # 点状干扰
)
# 验证码背景色(RGB)
CAPTCHA_BACKGROUND_COLOR = '#ffffff'
# 验证码文字颜色(RGB)
CAPTCHA_FOREGROUND_COLOR = '#001100'

3. 执行 python manage.py migrate 

4. 新增验证码表单

# 新建 project/forms.py
from django import forms
from django.contrib.admin.forms import AdminAuthenticationForm
from captcha.fields import CaptchaField


class AdminCaptchaAuthenticationForm(AdminAuthenticationForm):
    # 新增验证码字段,label 可自定义显示文本
    captcha = CaptchaField(label='验证码')

    # 保留原有的 admin 登录验证逻辑,仅新增验证码校验
    def clean(self):
        # 先执行父类的 clean 方法(验证用户名密码)
        cleaned_data = super().clean()
        # 验证码的校验由 CaptchaField 自动完成,无需额外处理
        return cleaned_data

5. 新增视图

# 新建 project/views.py
from django.contrib.admin.sites import AdminSite
from django.contrib.auth.views import LoginView
from .captcha_forms import AdminCaptchaAuthenticationForm


class AdminCaptchaLoginView(LoginView):
    # 指定自定义表单
    form_class = AdminCaptchaAuthenticationForm
    # 复用 admin 登录模板
    template_name = 'admin/login.html'

    def get_context_data(self, **kwargs):
        context = super().get_context_data(**kwargs)
        admin_site = AdminSite()
        context.update({
            'site_title': admin_site.site_title,
            'site_header': admin_site.site_header,
            'site_url': admin_site.site_url,
            'title': '管理员登录',
        })
        return context

6. 新增 login.html

# 新建 project/codenotes/templates/admin.py
<!-- templates/admin/login.html -->
{% extends "admin/base_site.html" %}
{% load i18n static %}

{% block extrastyle %}{{ block.super }}
    <link rel="stylesheet" href="{% static "admin/css/login.css" %}">
    {{ form.media }}  <!-- 新增:加载验证码所需的静态资源 -->
    <style>
        /* 控制验证码图片垂直对齐,与输入框居中 */
        .captcha {
            vertical-align: middle;
            margin-bottom: 3px;
            border: none;
        }

        /* 调整验证码输入框的高度,与图片更匹配 */
        input[name="captcha_1"] {
            height: 25px;
        }
    </style>
{% endblock %}

{% block bodyclass %}{{ block.super }} login{% endblock %}

{% block nav-breadcrumbs %}{% endblock %}

{% block content_title %}{% endblock %}

{% block content %}
    {% if form.errors and not form.non_field_errors %}
        <p class="errornote">
            {% blocktranslate count counter=form.errors.items|length %}Please correct the error below.{% plural %}Please
                correct the errors below.{% endblocktranslate %}
        </p>
    {% endif %}

    {% if form.non_field_errors %}
        {% for error in form.non_field_errors %}
            <p class="errornote">
                {{ error }}
            </p>
        {% endfor %}
    {% endif %}

    <div id="content-main">

        {% if user.is_authenticated %}
            <p class="errornote">
                {% blocktranslate trimmed %}
                    You are authenticated as {{ username }}, but are not authorized to access this page.
                    Would you like to login to a different account?
                {% endblocktranslate %}
            </p>
        {% endif %}

        <form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}
            <div class="form-row">
                {{ form.username.errors }}
                {{ form.username.label_tag }} {{ form.username }}
            </div>
            <div class="form-row">
                {{ form.password.errors }}
                {{ form.password.label_tag }} {{ form.password }}
                <input type="hidden" name="next" value="{{ next }}">
            </div>

            <!-- 新增:验证码字段渲染 -->
            <div class="form-row">
                {{ form.captcha.errors }}  <!-- 验证码错误提示 -->
                {{ form.captcha.label_tag }} {{ form.captcha }}  <!-- 验证码标签 + 输入框 + 图片 -->
            </div>

            {% url 'admin_password_reset' as password_reset_url %}
            {% if password_reset_url %}
                <div class="password-reset-link">
                    <a href="{{ password_reset_url }}">{% translate 'Forgotten your password or username?' %}</a>
                </div>
            {% endif %}
            <div class="submit-row">
                <input type="submit" value="{% translate 'Log in' %}">
            </div>
        </form>

    </div>
{% endblock %}

7. 添加路由

# urls
urlpatterns = [
    # 增加路由
    path('captcha/', include('captcha.urls')),
]

完成测试看是否成功。