django-simple-captcha验证码的使用

第三方库:django-simple-captcha

验证码实践目录

  • 第三方库:`django-simple-captcha`
  • 简单使用步骤
    • (一)安装配置captcha:
          • 1. 【Terminal】使用命令行安装
          • 2. 把captcha添加到Django项目下的settings.py文件已安装app中
          • 3. 【Terminal】迁移验证码所需数据库
          • 4. 添加captcha到urls.py中(在项目urls或者app的urls均可)
    • (二)将验证码添加到表单
          • 1. 定义验证码表单
          • 2. 验证表单方法定义
          • 3. 添加路由
          • 4. 定义前端页面
    • (三)效果展示
  • 部分问题
    • (一)前后端分离验证码如何传值
        • 1.问题描述
        • 2.需求字段
        • 3.现有项目

简单使用步骤

(一)安装配置captcha:

1. 【Terminal】使用命令行安装

pip install django-simple-captcha

2. 把captcha添加到Django项目下的settings.py文件已安装app中
INSTALLED_APPS = [
   ……
    'captcha',
]
3. 【Terminal】迁移验证码所需数据库

python manage.py migrate

  • 数据库迁移相关命令:
    python manage.py makemigrations 是用于检测app/models.py的改动,console会告知create model xxx
    python manage.py migrate将上述改动翻译成sql并去数据库执行

  • 验证码模型源码地址:venv/Lib/site-packages/captcha/models.py

    from captcha.conf import settings as captcha_settings
    from django.db import models
    from django.utils import timezone
    from six import python_2_unicode_compatible
    from django.utils.encoding import smart_text
    import datetime
    import hashlib
    import logging
    import random
    import time
    
    
    # Heavily based on session key generation in Django
    # Use the system (hardware-based) random number generator if it exists.
    if hasattr(random, 'SystemRandom'):
        randrange = random.SystemRandom().randrange
    else:
        randrange = random.randrange
    MAX_RANDOM_KEY = 18446744073709551616     # 2 << 63
    
    logger = logging.getLogger(__name__)
    
    
    @python_2_unicode_compatible
    class CaptchaStore(models.Model):
        challenge = models.CharField(blank=False, max_length=32)
        response = models.CharField(blank=False, max_length=32)
        hashkey = models.CharField(blank=False, max_length=40, unique=True)
        expiration = models.DateTimeField(blank=False)
    
        def save(self, *args, **kwargs):
            self.response = self.response.lower()
            if not self.expiration:
                self.expiration = timezone.now() + datetime.timedelta(minutes=int(captcha_settings.CAPTCHA_TIMEOUT))
            if not self.hashkey:
                key_ = (
                    smart_text(randrange(0, MAX_RANDOM_KEY)) +
                    smart_text(time.time()) +
                    smart_text(self.challenge, errors='ignore') +
                    smart_text(self.response, errors='ignore')
                ).encode('utf8')
                self.hashkey = hashlib.sha1(key_).hexdigest()
                del(key_)
            super(CaptchaStore, self).save(*args, **kwargs)
    
        def __str__(self):
            return self.challenge
    
        def remove_expired(cls):
            cls.objects.filter(expiration__lte=timezone.now()).delete()
        remove_expired = classmethod(remove_expired)
    
        @classmethod
        def generate_key(cls, generator=None):
            challenge, response = captcha_settings.get_challenge(generator)()
            store = cls.objects.create(challenge=challenge, response=response)
    
            return store.hashkey
    
        @classmethod
        def pick(cls):
            if not captcha_settings.CAPTCHA_GET_FROM_POOL:
                return cls.generate_key()
    
            def fallback():
                logger.error("Couldn't get a captcha from pool, generating")
                return cls.generate_key()
    
            # Pick up a random item from pool
            minimum_expiration = timezone.now() + datetime.timedelta(minutes=int(captcha_settings.CAPTCHA_GET_FROM_POOL_TIMEOUT))
            store = cls.objects.filter(expiration__gt=minimum_expiration).order_by('?').first()
    
            return (store and store.hashkey) or fallback()
    
        @classmethod
        def create_pool(cls, count=1000):
            assert count > 0
            while count > 0:
                cls.generate_key()
                count -= 1
    
     
    
  • 数据库表结构如下:
    django-simple-captcha验证码的使用_第1张图片

  • Django的captcha没有用session对验证码进行存储,而是在数据库中生成一张表进行存储页面加载时数据库生成一条关于验证码的记录。当提交表单时post到服务器,此时服务器验证captcha_captchastore表中的hashkey对应的response是否与输入的验证码一致。(看起来验证过程是自动完成的,在封装好的第三方库,所以后台代码也不需要处理验证码的验证工作)

4. 添加captcha到urls.py中(在项目urls或者app的urls均可)
	urlpatterns = [
		path ('captcha/', include ('captcha.urls')),  # 验证码 		
	]
  • 文档:

    urlpatterns += [
        url(r'^captcha/', include('captcha.urls')),
    ]
    
  • 补充url和path的区别:

    PATH(2.0):from django.urls import path——第三方框架/模块
    URL(1.0):from django.urls import url——自定义模块

(二)将验证码添加到表单

1. 定义验证码表单
  • 新建forms.py文件在app目录下(即models.py同级目录)
  • 可以选继承Form或者modelForm
#forms.py

from django import forms
from captcha.fields import CaptchaField

class CaptchaTestForm(forms.Form):
    captcha = CaptchaField()
2. 验证表单方法定义
# views.py

from user.forms import CaptchaTestForm
from django.shortcuts import render
def some_view(request):
    if request.POST:
        form = CaptchaTestForm(request.POST)
        # Validate the form: the captcha field will automatically
        # check the input
        if form.is_valid():
            human = True
    else:
        form = CaptchaTestForm()
    return render(request,'captcha.html', locals())
  • 必须import定义的表单和render(我的app名是user)
  • 文档用的render_to_response在现在新的django里面已经不支持,换成render可以使用
  • locals()返回函数执行到此节点内的所有局部变量
3. 添加路由

将上面新增的some_view方法加到urls路由

# user/urls.py
urlpatterns = [
    path ('captcha/', include ('captcha.urls')),  # 验证码
    ……
    path('demo',views.some_view),
]
4. 定义前端页面


<form method='POST'>
    {% csrf_token %}
    {
    { form }}
    <input type="submit" />
    <button class='js-captcha-refresh'>button>
form>
  • {% csrf_token %}不放在表单内,提交可能会产生错误
    django-simple-captcha验证码的使用_第2张图片
  • CSRF -跨站请求伪造保护
  • 表单没有添加action操作,因此验证通过还是回到原页面,错误会有提示

(三)效果展示

django-simple-captcha验证码的使用_第3张图片django-simple-captcha验证码的使用_第4张图片

参考文档:

  • django-simple-captcha.readthedocs.io

部分问题

(一)前后端分离验证码如何传值

1.问题描述

一直不知道验证码在前后端分离的时候,接口设计怎么使用,比如需要传什么数据,但是前端传递入口又依赖于django的captcha库,后端才接触captcha,那前端要怎么作用呢?就做了一个像下面一样小白的测试,但是captcha_0(一段输入的哈希值吧)怎么前端怎么拿传给后台呢?
django-simple-captcha验证码的使用_第5张图片

2.需求字段

登录表单定义了三个属性(见下个目录的代码),但是从控制台看有四个数据,验证码占两个:captcha_0(hashkey),captcha_1(用户输入)。

3.现有项目

项目结构大概如下(user是项目的app):
django-simple-captcha验证码的使用_第6张图片

  • templates/user/login.html


<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>登录title>
head>
<body>
<div>
    <form action="/user/formlogin" method="post">
        {% csrf_token %}
         
        <div>
            {
    { login_form.user_account.label_tag }}
            {
    { login_form.user_account }}
        div>
        <div>
            {
    { login_form.password.label_tag }}
            {
    { login_form.password }}
        div>
         
        <div>
            {
    { login_form.captcha.errors }}
            {
    { login_form.captcha.label_tag }}
            {
    { login_form.captcha }}
        div>
        <input type="submit" value="确定">
    form>
div>
body>
html>
  • user/forms.py
# forms.py
from django import forms
from captcha.fields import CaptchaField
from django.core.exceptions import ValidationError
from user.models import User


# 登录表单
class UserForm (forms.Form):
    user_account = forms.CharField (label="用户名", max_length=128, widget=forms.TextInput (attrs={
     'class': 'form-control'}))
    password = forms.CharField (label="密码", max_length=256,
                                widget=forms.PasswordInput (attrs={
     'class': 'form-control'}))
    captcha = CaptchaField (label='验证码')  # 图片验证码+输入框
  • user/views.py
# 用户登录,生成token
# 接收表单数据
# username 用户名
# password 密码
# user 通过输入的用户名获取的数据库用户
# url:user/formlogin
# 返回JSON数据
@csrf_exempt
def form_login(request):
    if request.method == "POST":
        login_form = UserForm (request.POST)  # 自定义登录表单
        if login_form.is_valid ():  # 确保用户名和密码都不为空
            username = login_form.cleaned_data['user_account']
            password = login_form.cleaned_data['password']
            print (username + password)
            try:
                user = User.objects.get (user_account=username)
            except:
                return HttpResponse (json.dumps ({
     'code': '301'}))  # 用户不存在
            if check_password (password, user.password):
                # print ('数据库密码是:' + user.password)
                temp = user
                token = token_op.create_token (temp)
                return HttpResponse (
                    json.dumps (
                        {
     
                            'code': '200',  # 成功状态码
                            'token': token,
                            'user_id': user.user_id,
                            'user_account': user.user_account,
                            'user_url': user.user_url,
                            'user_name': user.user_name,
                            'user_gender': user.user_gender,
                            'email': user.email,
                            'user_phone': user.user_phone,
                            'user_credit': user.user_credit,
                            'status': user.status,
                        }
                    )
                )
            else:
                return HttpResponse (json.dumps ({
     'code': '302'}))  # 密码不正确
        else:
            return HttpResponse (json.dumps ({
     'code': '402'}))  # 输入框未填完
    else:
        # login_form = UserForm ()
        # return HttpResponse (json.dumps ({'code': '404'}))  # 请求非POST类型
        return render(request, 'user/login.html', locals())
  • user/urls.py 添加路由
path ('formlogin', loginViews.form_login),  # html页面

你可能感兴趣的:(Django的web项目,Django验证码,captcha)