一、环境配置
1. 运行环境
- Python 3.10
- Django 4.2.20
- django-redis == 5.4.0
- captcha==0.7.1
- Pillow==11.2.1
- django_ratelimit == 4.1.0
# 安装指定版本
pip install captcha django-redis pillow
二、项目配置(settings.py
)
# Redis缓存配置
CACHES = {
"default": {
"BACKEND": "django_redis.cache.RedisCache",
"LOCATION": "redis://127.0.0.1:6379/1",
"OPTIONS": {
"CLIENT_CLASS": "django_redis.client.DefaultClient",
"SOCKET_CONNECT_TIMEOUT": 5,
}
}
}
# 验证码参数
CAPTCHA_LENGTH = 4 # 4位字符
CAPTCHA_TIMEOUT = 300 # 5分钟过期
CAPTCHA_IMAGE_SIZE = (120, 40) # 图片尺寸
CAPTCHA_CACHE = 'default' # 图形验证码存放库
三、核心实现
1. 生成验证码
# utils/captcha.py
import random
import base64
import uuid
import string
from loguru import logger
from captcha.image import ImageCaptcha
from django_redis import get_redis_connection
from django.conf import settings
class __CaptchaService:
# 生成验证码尺寸
__image_generator = ImageCaptcha(
width=settings.CAPTCHA_IMAGE_SIZE[0],
height=settings.CAPTCHA_IMAGE_SIZE[1]
)
def __init__(self):
# 获取连接redis,指定库
self.__redis = get_redis_connection(settings.CAPTCHA_CACHE)
def generate_captcha(self):
"""生成验证码图片及UUID"""
# 生成随机数字
code = ''.join(random.choices(string.ascii_letters + string.digits, k=settings.CAPTCHA_LENGTH))
# 生成唯一标识
key = str(uuid.uuid4())
# 存储到Redis
self.__redis.setex(f'captcha:{key}', settings.CAPTCHA_TIMEOUT, code)
# 生成图片
image = self.__image_generator.generate(code)
img_base64 = base64.b64encode(image.getvalue()).decode()
return {
'key': key,
'image': f'data:image/png;base64,{img_base64}'
}
def validate_captcha(self, key, user_input):
"""验证码校验"""
logger.debug(f'validate_captcha >>> key: {key} - code: {user_input}')
stored_code = self.__redis.get(f'captcha:{key}')
if not stored_code:
return False
# 删除已使用的验证码
self.__redis.delete(f'captcha:{key}')
return stored_code.decode().lower() == user_input.strip().lower()
captcha = __CaptchaService()
- 存储样例:
四、视图层(views.py
)
from django.http import JsonResponse
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_http_methods
from utils.captcha import captcha
from django_ratelimit.decorators import ratelimit
from django.shortcuts import render
def index(request):
return render(request, 'index.html')
@ratelimit(key='ip', rate='5/m')
@require_http_methods(["GET"])
def get_captcha(request):
"""获取验证码接口"""
try:
data = captcha.generate_captcha()
return JsonResponse({
'code': 200,
'data': data
})
except Exception as e:
return JsonResponse({
'code': 500,
'msg': str(e)
}, status=500)
@csrf_exempt
@ratelimit(key='ip', rate='10/m')
@require_http_methods(["POST"])
def verify_captcha(request):
"""验证码校验接口"""
key = request.POST.get('key')
code = request.POST.get('code', '')
if not all([key, code]):
return JsonResponse({
'code': 400,
'msg': '参数缺失'
}, status=400)
if captcha.validate_captcha(key, code):
return JsonResponse({'code': 200})
return JsonResponse({
'code': 400,
'msg': '验证码错误'
})
五、前端交互(jQuery示例)
// index.html
<!DOCTYPE html>
{% load static %}
<html lang="zh-CN">
<head>
<title>test</title>
<script src="{% static 'js/jquery-3.6.0.js' %}"></script>
</head>
<body>
<div class="captcha-box">
<label for="captchaInput">验证码</label>
<input type="text" id="captchaInput" placeholder="输入验证码">
<img id="captchaImage" src="#" style="height:40px; cursor:pointer;" alt="获取失败">
<button onclick="refreshCaptcha()">刷新</button>
<button onclick="verifyCaptcha()">验证</button>
</div>
<script>
let captchaKey = '';
// 加载验证码
function loadCaptcha() {
$.ajax({
url: '/get_captcha/',
success: function (res) {
if (res.code === 200) {
captchaKey = res.data.key;
$('#captchaImage').attr('src', res.data.image);
}
}
});
}
// 验证提交
function verifyCaptcha() {
const code = $('#captchaInput').val();
$.ajax({
url: '/verify_captcha/',
method: 'POST',
data: {
key: captchaKey,
code: code
},
success: function (res) {
alert(res.code === 200 ? '验证成功' : '验证失败');
loadCaptcha(); // 无论成功失败都刷新
}
});
}
function refreshCaptcha(){
loadCaptcha();
}
// 初始化加载
$(document).ready(loadCaptcha);
</script>
</body>
</html>
六、路由配置
# urls.py
urlpatterns = [
path('index/', views.index, name='index'),
path('get_captcha/', views.get_captcha, name='get_captcha'),
path('verify_captcha/', views.verify_captcha, name='verify_captcha'),
]
七、运行测试
-
启动Redis服务
redis-server
-
启动Django
python manage.py runserver
-
访问
http://localhost:8000
测试功能- 点击图片刷新验证码
- 输入4位数字提交验证
-
方案特点
- 零数据库依赖:完全使用
Redis
存储,无需任何数据库表 - 高性能:验证码生成+存储耗时<20ms(i5-1135G7测试)
- 版本兼容:完全适配
captcha 0.7.1
的API - 安全机制:
- 验证码单次有效
- IP请求频率限制(5次/分钟)
- 自动清理Redis过期数据
- 零数据库依赖:完全使用
八、性能优化建议
- 预生成验证码池
class CaptchaManager:
def __init__(self):
self.pool = Queue(maxsize=100) # 预生成100个验证码
def _pre_generate(self):
while True:
if self.pool.qsize() < 100:
self.pool.put(self.generate())
- 异步生成(使用Celery)
@app.task
def async_generate_captcha():
return CaptchaManager().generate()