为什么80%的码农都做不了架构师?>>>   

历史原因,使用上古版本 django 1.6.5,但新版本应该大同小异

首先添加自定义后台模块app, 如adm,并添加到 INSTALLED_APPS 下。

假设处理自定义登录的view是 apps/adm/views/custom_admin.py 中的 custom_login 函数

配置url:


from django.contrib import admin
from adm.views.custom_admin import custom_loginadmin.autodiscover()
admin.site.login = custom_loginurlpatterns += (url(r'^custom-admin/', include(admin.site.urls)),url(r'^custom-admin/', include('adm.urls')),
)

登录页中有两种形式,一种是本地和测试环境, 输入用户名加密码加captch三项,然后登录,此处的captcha都是修改的django的表单,不少代码是搬运的django-simple-captcha,因为这个库使用的话还需要添加它的model进行migrate,所以没直接使用;另一种是线上环境,需要输入用户名(手机号)加密码加短信验证码三项,并且短信验证码的需要点击获取,正确输入弹出的captcha并且用户名在setting中的白名单才可以获取,此处的captcha是后台的api获取,而非表单。

view如下(需要自己实现生成captcha图片和发送短信的api):


from django.contrib.auth import REDIRECT_FIELD_NAME, login as auth_login
from django.contrib.sites.models import get_current_site
from django.http import HttpResponseRedirect, HttpResponse
from django.template.response import TemplateResponse
from django.views.decorators.cache import never_cache
from django.views.decorators.csrf import csrf_protect
from django.views.decorators.debug import sensitive_post_parameters
from django.utils.encoding import force_textfrom adm.forms import SmscodeAdminAuthenticationForm, MultiCaptchaAdminAuthenticationForm
from settings import  ADMIN_ENABLE_SMS_LOGIN, ADMIN_PHONES, ADMIN_SHOW_DELETE_SELECTEDif ADMIN_ENABLE_SMS_LOGIN:admin.AdminSite.login_form = SmscodeAdminAuthenticationFormAuthForm = SmscodeAdminAuthenticationForm
else:admin.AdminSite.login_form = MultiCaptchaAdminAuthenticationFormAuthForm = MultiCaptchaAdminAuthenticationForm# 根据设置禁用批量删除
if not ADMIN_SHOW_DELETE_SELECTED:admin.site.disable_action('delete_selected')@sensitive_post_parameters()
@csrf_protect
@never_cache
def custom_login(request, template_name='custom_login.html',redirect_field_name=REDIRECT_FIELD_NAME,authentication_form=AuthForm,current_app=None, extra_context=None):redirect_to = request.REQUEST.get(redirect_field_name, '')if request.method == "POST":ua = request.META['HTTP_USER_AGENT'].strip()ip = request.META['REMOTE_ADDR']username = request.POST.get('username')logtrade.info('try login. username:%s' % username)if username not in ADMIN_PHONES:password = request.POST.get('password')logging.error(u'登录失败! 用户名不在白名单:%s, input password:%s, IP:%s, UserAgent:%s' % (username, password, ip, ua))form = authentication_form(request, data=request.POST)if form.is_valid():auth_login(request, form.get_user())return HttpResponseRedirect(redirect_to)else:form = authentication_form(request)current_site = get_current_site(request)context = {'form': form,redirect_field_name: redirect_to,'site': current_site,'site_name': current_site.name,'sms_login': ADMIN_ENABLE_SMS_LOGIN,}if extra_context is not None:context.update(extra_context)return TemplateResponse(request, template_name, context,current_app=current_app)

其中两个表单的实现如下:
大部分功能使用了django-simple-captcha的代码,主要的改动是把captcha文本存在缓存里,使用session_key关联,取消了数据库的使用

# encoding=utf-8from django.contrib.admin.forms import AdminAuthenticationFormfrom django.core.exceptions import ImproperlyConfigured
from django.core.urlresolvers import reverse, NoReverseMatch
from django import forms
from django.forms import ValidationError
from django.forms.fields import CharField, MultiValueField
from django.forms.widgets import TextInput, MultiWidget, HiddenInput
from django.utils.translation import ugettext, ugettext_lazy
from six import u
import base64
from django.core.cache import cache
from settings import ADMIN_PHONES# utils为自己实现的库,包括生成captcha,发送短信等
from utils.captcha_util import get_captcha_by_text, get_code_text, compare_code_ignore_case
from utils.sms_util import verify_sms_codeCAPTCHA_OUTPUT_FORMAT = u'%(image)s %(hidden_field)s %(text_field)s'def get_captcha(key):return cache.get('captcha_' + key)def set_captcha(key):captcha_text = get_code_text()  # 获取captcha文本cache.set('captcha_' + key, captcha_text, 30 * 60)def delete_captcha(key):cache.delete('captcha_' + key)class BaseCaptchaTextInput(MultiWidget):"""Base class for Captcha widgets"""def __init__(self, attrs=None):self.session_key = attrs.get('session_key')widgets = (HiddenInput(attrs),TextInput(attrs),)super(BaseCaptchaTextInput, self).__init__(widgets, attrs)def decompress(self, value):if value:return value.split(',')return [None, None]def fetch_captcha_store(self, name, value, attrs=None):"""Fetches a new CaptchaStoreThis has to be called inside render"""#key = CaptchaStore.generate_key()key = self.session_key  # 用session_key关联captchaset_captcha(key)# these can be used by format_output and renderself._value = [key, u('')]self._key = keyself.id_ = self.build_attrs(attrs).get('id', None)def id_for_label(self, id_):if id_:return id_ + '_1'return id_def image_url(self):# 返回的不是url而是图片的base64编码text = get_captcha(self.session_key)captcha_content = get_captcha_by_text(text)return base64.b64encode(captcha_content)def refresh_url(self):return reverse('captcha-refresh')class CaptchaTextInput(BaseCaptchaTextInput):def __init__(self, attrs=None, **kwargs):self._args = kwargsself._args['output_format'] = self._args.get('output_format') or CAPTCHA_OUTPUT_FORMATself._args['id_prefix'] = self._args.get('id_prefix')for key in ('image', 'hidden_field', 'text_field'):if '%%(%s)s' % key not in self._args['output_format']:raise ImproperlyConfigured('All of %s must be present in your CAPTCHA_OUTPUT_FORMAT setting. Could not find %s' % (', '.join(['%%(%s)s' % k for k in ('image', 'hidden_field', 'text_field')]),'%%(%s)s' % key))super(CaptchaTextInput, self).__init__(attrs)def build_attrs(self, extra_attrs=None, **kwargs):ret = super(CaptchaTextInput, self).build_attrs(extra_attrs, **kwargs)if self._args.get('id_prefix') and 'id' in ret:ret['id'] = '%s_%s' % (self._args.get('id_prefix'), ret['id'])return retdef id_for_label(self, id_):ret = super(CaptchaTextInput, self).id_for_label(id_)if self._args.get('id_prefix') and 'id' in ret:ret = '%s_%s' % (self._args.get('id_prefix'), ret)return retdef format_output(self, rendered_widgets):hidden_field, text_field = rendered_widgetstext_field = text_field.replace('<input', '<input autocomplete="off"')return self._args['output_format'] % {'image': self.image,'hidden_field': hidden_field,'text_field': text_field}def render(self, name, value, attrs=None):self.fetch_captcha_store(name, value, attrs)self.image = '<img src="https://img-blog.csdnimg.cn/2022010623403229180.png" alt="captcha" class="captcha" />' % self.image_url()return super(CaptchaTextInput, self).render(name, self._value, attrs=attrs)class CaptchaField(MultiValueField):def __init__(self, session_key=None, *args, **kwargs):self.session_key = session_keyfields = (CharField(show_hidden_initial=True),CharField(),)if 'error_messages' not in kwargs or 'invalid' not in kwargs.get('error_messages'):if 'error_messages' not in kwargs:kwargs['error_messages'] = {}kwargs['error_messages'].update({'invalid': ugettext_lazy('Invalid CAPTCHA')})attrs = {'session_key': session_key}kwargs['widget'] = kwargs.pop('widget', CaptchaTextInput(attrs=attrs,output_format=kwargs.pop('output_format', None),id_prefix=kwargs.pop('id_prefix', None)))super(CaptchaField, self).__init__(fields, *args, **kwargs)def compress(self, data_list):if data_list:return ','.join(data_list)return Nonedef clean(self, value):super(CaptchaField, self).clean(value)response, value[1] = (value[1] or '').strip().lower(), ''correct_captcha = cache.get('captcha_' + self.session_key)if not compare_code_ignore_case(response, correct_captcha):raise ValidationError(getattr(self, 'error_messages', {}).get('invalid', ugettext_lazy('Invalid CAPTCHA')))delete_captcha(self.session_key)return valueclass MultiCaptchaAdminAuthenticationForm(AdminAuthenticationForm):def __init__(self, request=None, *args, **kwargs):if not request.session.session_key:request.session.create()session_key = request.session.session_keysuper(MultiCaptchaAdminAuthenticationForm, self).__init__(*args, **kwargs)self.fields['captcha'] = CaptchaField(session_key=session_key)class SmscodeField(CharField):"""短信验证码字段"""def __init__(self, cellphone=None, *args, **kwargs):self.cellphone = cellphoneself.required = Truesuper(SmscodeField, self).__init__(*args, **kwargs)def clean(self, value):super(SmscodeField, self).clean(value)# 登录用户名必须是setting白名单中设置的手机号if self.cellphone not in ADMIN_PHONES:params = {'username': u"用户名"}raise forms.ValidationError(u'用户名或密码有误', code='invalid', params=params)if not verify_sms_code(cellphone=self.cellphone, sms_code=value):params = {'smscode': u'短信验证码'}raise forms.ValidationError(u'短信验证码有误', code='invalid', params=params)class SmscodeAdminAuthenticationForm(AdminAuthenticationForm):def __init__(self, request=None, *args, **kwargs):if not request.session.session_key:request.session.create()super(SmscodeAdminAuthenticationForm, self).__init__(*args, **kwargs)self.fields['smscode'] = SmscodeField(cellphone=self.data.get('username'))

登录页的模板 custom_login.html, 找前端同学写的js和样式:

{% extends "admin/base_site.html" %}
{% load i18n admin_static %}{% block extrastyle %}
{{ block.super }}<link rel="stylesheet" type="text/css" href="{% static "admin/css/login.css" %}" />
<link rel="stylesheet" type="text/css" href="{% static "css/custom_form.css" %}" />
{{ block.super }}{% endblock %}{% block bodyclass %}login{% endblock %}{% block nav-global %}{% endblock %}{% block content_title %}{% endblock %}{% block breadcrumbs %}{% endblock %}{% block content %}
{% if form.errors and not form.non_field_errors and not form.this_is_the_login_form.errors %}
<p class="errornote">
{% if form.errors.items|length == 1 %}{% trans "Please correct the error below." %}{% else %}{% trans "Please correct the errors below." %}{% endif %}
</p>
{% endif %}{% if form.non_field_errors or form.this_is_the_login_form.errors %}
{% for error in form.non_field_errors|add:form.this_is_the_login_form.errors %}
<p class="errornote">{{ error }}
</p>
{% endfor %}
{% endif %}<div id="content-main"><form action="{{ app_path }}" method="post" id="login-form">{% csrf_token %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.username.errors }}{% endif %}<label for="id_username" class="required">{{ form.username.label }}:</label> {{ form.username }}</div><div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.password.errors }}{% endif %}<label for="id_password" class="required">{% trans 'Password:' %}</label> {{ form.password }}<input type="hidden" name="this_is_the_login_form" value="1" /><input type="hidden" name="next" value="{{ next }}" /></div>{% if sms_login %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.smscode.errors }}{% endif %}<label for="smscode" class="required">短信验证码: </label><a href="javascript:void(0);" class="getSmscode" id="getSmscode" data-click="0" onclick="getMsgCode()">点击获取短信验证码</a><div><th width="920">{{ form.smscode }}</th></div></div>{% else %}<div class="form-row">{% if not form.this_is_the_login_form.errors %}{{ form.captcha.errors }}{% endif %}{{ form.captcha }}</div>{% endif %}<div class="submit-row"><label>&nbsp;</label><input type="submit" value="{% trans 'Log in' %}" /></div>
</form><div class="captcha-cover"><div class="form-row"><div class="captchaImg"><img id="captchaImg" src="" alt="" onclick="getCaptcha()"><span onclick="getCaptcha()">点击更换图片</span></div><div class="text-box"><input placeholder="请输入上图中的验证码" id="captchaText" value=""></div><div class="control-btn"><a href="javascript:void 0;" onclick="hideModal()">取消</a><div class="line"></div><a href="javascript:void 0;" class="current" onclick="getSmscode()">确定</a></div></div>
</div><script type="text/javascript" src="{% static "admin/js/jquery.min.js" %}"></script><script type="text/javascript">
{% if sms_login %}// 获取图片验证码
function getCaptcha() {var captchaUrl = "/custom-admin/captcha";$.ajax({type: "GET",url: captchaUrl,dataType: "json",success: function (data) {if (data.ret) {document.getElementById("captchaImg").src = "data:image/png;base64," + data.captcha;} else {console.log(data.msg);}}})
}//弹窗的显示和隐藏
function getMsgCode(){$(".captcha-cover").css("display","block");getCaptcha();
}
function hideModal(){$(".captcha-cover").css("display","none");
}$("#captchaText").on("keypress",function(event){if(event.keyCode===13){getSmscode();}
})// 获取短信
function getSmscode(){var smscodeUrl = "/custom-admin/smscode";var captchaText = document.getElementById("captchaText").value;var cellphone = document.getElementById("id_username").value;var data = {'captcha': captchaText,'cellphone': cellphone,};$.ajax({type: "POST",url: smscodeUrl,dataType: "json",data: data,success: function (data) {if (data.ret) {hideModal();} else {alert(data.msg);getCaptcha();}}})
}
{% endif %}
</script>
</div>
{% endblock %}

样式:

.login .form-row #id_smscode {clear: both;padding: 6px;width: 100%;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;
}.captcha-cover{display: none;position:fixed;left:0;top:0;width:100%;height:1000px;background-color:rgba(0,0,0,0.5);text-align:center;
}.captcha-cover .form-row{padding-top:40px;display: inline-block;float: inherit!important;width:320px;margin:150px auto;background-color:#fff;padding-bottom:0;
}
.captchaImg{padding:0 30px;display: inline-block;margin-bottom:10px;
}
#captchaImg{float:left;display:inline-block;width: 100%;padding:6px 2px;border:1px solid #000;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;vertical-align:middle;
}
#captchaText{display:inline-block;vertical-align:middle;width:100%;-webkit-box-sizing: border-box;-moz-box-sizing: border-box;box-sizing: border-box;
}
.captchaImg span{cursor:pointer;margin-left:20px;display: inline-block;line-height:40px;
}
#captchaImg{padding:2px 10px;width:100px;height:38px;
}
.text-box{padding:0 50px;
}
#captchaText{height:40px;box-sizing: border-box;
}
.control-btn{position:relative;margin-top:20px;border-top:1px solid #ededed;
}
.control-btn a{display: inline-block;width:49%;text-align:center;box-sizing: border-box;font-size:14px;line-height:44px;color:#666;
}
.control-btn a.current{color:dodgerblue;
}
.line{position:absolute;top:-1px;left:50%;width:1px;height:45px;background-color:#ededed;
}

最后效果如下

转载于:https://my.oschina.net/sukai/blog/1604584

为 Django admin 登录页添加验证码相关推荐

  1. Vue登录页添加验证码

    登录页添加验证码 新建ValidCode组件 ​ 内容如下,直接copy即可: <template><divclass="ValidCode disabled-select ...

  2. django admin 登陆快速添加验证码功能

    1. 安装包 pip install django-multi-captcha-admin django-simple-captcha 2. setting.py INSTALLED_APPS = [ ...

  3. Django admin登录页面验证码(1):普通字符和算术验证码

    1.前言 django的登录界面默认只有用户名和密码输入框,没有额外的安全防护,如果在生产环境中不加登录验证码直接使用,是非常危险的,因为攻击者可以用特定程序不断的进行登录尝试,直至得出正确的登录密码 ...

  4. cas服务器登录页面添加验证码

    本文的是以cas-4.1.5进行的,cas源代码下载官网:https://apereo.github.io/cas/4.2.x/index.html. 1.在cas工程的web.xml增加验证码功能的 ...

  5. django admin 登录用户名密码错误提示

    项目的登录页是前同事自定义的,登录时用户名和密码错误时没有提示,体验不太好. 所以想加一个提示,刚开始看的django-suit的login.html源码没搞对,又看了一下django的login.h ...

  6. thinkphp 3.2 =》0625-6_登录业务与 验证码 =》登录页 与 验证码

    1.登录控制器  Application/Admin/Controller/ManagerController.class.php <?php /**  * Created by PhpStor ...

  7. 登录验证---添加验证码验证,Cookie保存功能

    1,登录表单,login.jsp View Code 1 <%@ page contentType="text/html" pageEncoding="GBK&qu ...

  8. vue项目web前端登录页数字验证码 登录流程

    1.创建code.js文件夹下面是js代码 function GVerify(options) {console.log(); // 创建一个图形验证码对象,接收options对象为参数this.co ...

  9. WordPress自定义登录和注册页面样式并且添加验证码

    通过一番尝试和验证以及网络搜索,终于实现了WordPress默认的登录和注册界面的样式修改,下面把过程记录下来.笔者主要是通过在主题中添加WordPress的钩子来实现登录和注册界面的样式修改的.Wo ...

最新文章

  1. 基于jwt的用户登录认证
  2. Linux命令初识一
  3. C Screen Shot Implementation
  4. 华硕飞行堡垒开启虚拟化
  5. Javaweb基础——Servlet
  6. JavaScipt面向对象编程----闭包
  7. Java 的日子屈指可数,这是真的吗?
  8. 使用Java 8.0进行类型安全的依赖项注入
  9. @Select注解的使用
  10. 图解带你掌握`JVM`运行时核心内存区
  11. Python回归 岭回归(Ridge Regression)
  12. C# 委托与事件(delegate)
  13. 杀猪、打狗、护牛、赛马、放虎——几种管理方法
  14. 2017小象学院Python数据分析与挖掘
  15. Bugku之秋名山老司机
  16. 选中exchange缓存模式后 GAL不会更新
  17. 电脑tcp协议设置成服务器,电脑tcp协议设置成服务器
  18. 联合分析法(Python实现)
  19. 北京筑龙CTO吴英礼受邀在清华大学互联网产业研究院做分享
  20. 关于poi word 文档生成的那些坑坑洼洼

热门文章

  1. 【linux】shell中浮点数运算的加、减、乘、除
  2. 【linux工具】ldconfig:linux配置动态链接库
  3. #Ruby# Introspect (1)
  4. java 读出数字声音_【求助】已经计算出的数字结果,想用声音播放出来。怎么弄!!!...
  5. 计算机网络7层协议模型,计算机网络(一) OSI七层模型及TCP/IP dubbo协议
  6. cssbefore图片大小_两小时学会CSS-before after 伪元素
  7. mac上mysql关闭不了了_python操作mysql数据库
  8. python3.7.1安装教程详细_CentOS 7 安装python3.7.1的方法及注意事项
  9. linux开发log示例,RH124-log Linux日志(示例代码)
  10. linux rm 提示io异常,Hadoop异常 java.io.IOException: Job status not available