Python中三大主流圖片驗(yàn)證碼庫使用推薦與實(shí)踐指南
前言
最近在做一個(gè) Web 項(xiàng)目的時(shí)候,需要添加驗(yàn)證碼功能來防止惡意注冊(cè)和破解。剛開始想著自己寫一個(gè)簡(jiǎn)單的驗(yàn)證碼生成器,但發(fā)現(xiàn)要考慮的東西還挺多的:圖片生成、字符扭曲、干擾線、過期時(shí)間、安全性等等。后來發(fā)現(xiàn)其實(shí)有很多現(xiàn)成的庫可以用,但選擇哪個(gè)庫、怎么集成到項(xiàng)目中,又是一個(gè)問題。
相信很多 Python 開發(fā)者都遇到過類似的問題:項(xiàng)目需要驗(yàn)證碼功能,但不知道選哪個(gè)庫,也不知道怎么集成。今天我們就來聊聊 Python 中常用的圖片驗(yàn)證碼庫,以及如何在實(shí)際項(xiàng)目中應(yīng)用它們。
為什么需要驗(yàn)證碼
在深入討論具體的庫之前,我們先聊聊為什么需要驗(yàn)證碼,以及在實(shí)際開發(fā)中會(huì)遇到哪些痛點(diǎn)。
常見的安全問題
現(xiàn)在的 Web 應(yīng)用面臨很多安全威脅,驗(yàn)證碼是其中一種重要的防護(hù)手段:
惡意注冊(cè):很多網(wǎng)站都會(huì)遇到惡意注冊(cè)的問題,有人用腳本批量注冊(cè)賬號(hào),占用服務(wù)器資源,甚至用來發(fā)送垃圾信息。如果沒有驗(yàn)證碼,這些腳本可以輕松地自動(dòng)化注冊(cè)流程。
惡意破解:對(duì)于登錄功能,如果沒有驗(yàn)證碼,攻擊者可以用腳本嘗試大量的用戶名密碼組合。雖然現(xiàn)在很多系統(tǒng)都有登錄失敗次數(shù)限制,但驗(yàn)證碼可以進(jìn)一步增加攻擊成本。
接口濫用:很多 API 接口如果沒有驗(yàn)證碼保護(hù),可能會(huì)被惡意調(diào)用,比如發(fā)送短信驗(yàn)證碼、發(fā)送郵件等。這些接口如果被濫用,不僅會(huì)消耗資源,還可能產(chǎn)生費(fèi)用。
爬蟲防護(hù):雖然驗(yàn)證碼不能完全阻止爬蟲,但可以增加爬蟲的成本。對(duì)于一些簡(jiǎn)單的爬蟲,驗(yàn)證碼就能起到很好的防護(hù)作用。
開發(fā)中的痛點(diǎn)
在實(shí)際開發(fā)中,實(shí)現(xiàn)驗(yàn)證碼功能會(huì)遇到很多痛點(diǎn):
圖片生成復(fù)雜:如果要自己實(shí)現(xiàn)驗(yàn)證碼生成,需要考慮很多細(xì)節(jié):字體選擇、字符扭曲、干擾線、干擾點(diǎn)、顏色搭配等等。這些細(xì)節(jié)處理不好,驗(yàn)證碼要么太簡(jiǎn)單容易被識(shí)別,要么太復(fù)雜用戶體驗(yàn)不好。
安全性問題:驗(yàn)證碼的安全性是一個(gè)大問題。如果驗(yàn)證碼太簡(jiǎn)單,容易被 OCR 識(shí)別;如果驗(yàn)證碼太復(fù)雜,用戶體驗(yàn)不好。而且還要考慮驗(yàn)證碼的過期時(shí)間、一次性使用、防止重放攻擊等問題。
框架集成:不同的 Web 框架(Flask、Django、FastAPI 等)集成驗(yàn)證碼的方式不一樣,需要針對(duì)性地適配。而且還要考慮前后端分離的場(chǎng)景,驗(yàn)證碼如何通過 API 返回。
用戶體驗(yàn):驗(yàn)證碼的用戶體驗(yàn)也很重要。如果驗(yàn)證碼看不清,用戶會(huì)抱怨;如果驗(yàn)證碼刷新不方便,用戶會(huì)煩躁。而且現(xiàn)在很多用戶習(xí)慣使用移動(dòng)端,驗(yàn)證碼在小屏幕上的顯示效果也要考慮。
維護(hù)成本:如果自己實(shí)現(xiàn)驗(yàn)證碼功能,后續(xù)的維護(hù)成本也不低。比如要更新字體、調(diào)整樣式、修復(fù) bug 等等。而使用現(xiàn)成的庫,可以降低維護(hù)成本。
主流圖片驗(yàn)證碼庫推薦
根據(jù)當(dāng)前的技術(shù)趨勢(shì),下面是最常用且好用的圖片驗(yàn)證碼庫,以及它們的特點(diǎn)和適用場(chǎng)景。
captcha:Python 原生庫,推薦度高
captcha 是一個(gè)由 Google 開發(fā)維護(hù)的 Python 庫,GitHub 上有 1.2k+ stars。它的特點(diǎn)是簡(jiǎn)單易用,支持自定義,適合各種 Python Web 框架。
優(yōu)點(diǎn):
- 簡(jiǎn)單易用,API 設(shè)計(jì)清晰
- 支持自定義圖片大小、字體、顏色等
- 不依賴特定的 Web 框架,可以在 Flask、Django、FastAPI 等框架中使用
- 由 Google 維護(hù),代碼質(zhì)量有保障
缺點(diǎn):
- 功能相對(duì)簡(jiǎn)單,不支持復(fù)雜的驗(yàn)證碼樣式
- 安全性相對(duì)較低,容易被 OCR 識(shí)別
適用場(chǎng)景:
- 內(nèi)部系統(tǒng)或中小型項(xiàng)目
- 對(duì)安全性要求不是特別高的場(chǎng)景
- 需要快速集成驗(yàn)證碼功能的項(xiàng)目
基本使用:
from captcha.image import ImageCaptcha
# 創(chuàng)建驗(yàn)證碼圖像
image = ImageCaptcha(width=280, height=90)
data = image.generate('1234')
image.write('1234', 'out.png')
這個(gè)庫的使用非常簡(jiǎn)單,只需要幾行代碼就能生成驗(yàn)證碼圖片。但需要注意的是,它生成的驗(yàn)證碼相對(duì)簡(jiǎn)單,安全性不是特別高。
django-simple-captcha:Django 專屬方案
django-simple-captcha 是專門為 Django 框架設(shè)計(jì)的驗(yàn)證碼庫,GitHub 上有 1.6k+ stars。它的特點(diǎn)是 Django 集成度最高,開箱即用。
優(yōu)點(diǎn):
- 與 Django 深度集成,使用非常方便
- 支持 Django Forms,可以直接在表單中使用
- 功能完善,支持多種驗(yàn)證碼樣式
- 社區(qū)活躍,文檔完善
缺點(diǎn):
- 僅限 Django 項(xiàng)目使用
- 樣式相對(duì)固定,自定義程度有限
適用場(chǎng)景:
- Django 項(xiàng)目
- 需要快速集成驗(yàn)證碼功能的 Django 應(yīng)用
- 不需要太多自定義的場(chǎng)景
基本使用:
# settings.py
INSTALLED_APPS = [
'captcha',
]
# models.py
from django import forms
from captcha.fields import CaptchaField
class ContactForm(forms.Form):
captcha = CaptchaField()
這個(gè)庫最大的優(yōu)勢(shì)就是與 Django 的集成非常好,如果你用的是 Django 框架,這個(gè)庫是最佳選擇。
kaptcha:Java 轉(zhuǎn) Python 實(shí)現(xiàn)
kaptcha 是模仿 Java 版 Kaptcha 的 Python 實(shí)現(xiàn),功能相對(duì)強(qiáng)大。
優(yōu)點(diǎn):
- 功能強(qiáng)大,支持多種驗(yàn)證碼樣式
- 可以生成復(fù)雜的驗(yàn)證碼圖片
缺點(diǎn):
- 文檔相對(duì)較少
- 社區(qū)活躍度不高
- 使用相對(duì)復(fù)雜
適用場(chǎng)景:
- 需要復(fù)雜驗(yàn)證碼樣式的項(xiàng)目
- 對(duì) Java Kaptcha 熟悉的開發(fā)者
商業(yè)方案:滑動(dòng)驗(yàn)證碼和行為驗(yàn)證碼
除了開源的庫,還有一些商業(yè)方案,比如極驗(yàn)、騰訊云驗(yàn)證碼、阿里云驗(yàn)證碼等。這些方案通常提供滑動(dòng)驗(yàn)證碼、行為驗(yàn)證碼等更高級(jí)的驗(yàn)證方式。
極驗(yàn)(geetest):
- 識(shí)別率高,安全性強(qiáng)
- 支持多種驗(yàn)證方式(滑動(dòng)、點(diǎn)選、語音等)
- 有免費(fèi)額度,超出后收費(fèi)
騰訊云驗(yàn)證碼:
- 智能驗(yàn)證,多種形式
- 與騰訊云服務(wù)集成
- 按調(diào)用次數(shù)收費(fèi)
阿里云驗(yàn)證碼:
- 風(fēng)險(xiǎn)識(shí)別,無感驗(yàn)證
- 與阿里云服務(wù)集成
- 按調(diào)用次數(shù)收費(fèi)
適用場(chǎng)景:
- 對(duì)安全性要求很高的商業(yè)項(xiàng)目
- 有預(yù)算支持的項(xiàng)目
- 需要高級(jí)驗(yàn)證方式的場(chǎng)景
Python 項(xiàng)目引入指南
下面我們來看看如何在實(shí)際項(xiàng)目中引入和使用這些驗(yàn)證碼庫。
基礎(chǔ)安裝配置
首先,我們需要安裝相應(yīng)的庫:
# 安裝 captcha 庫(Flask/FastAPI 通用) pip install captcha pillow # 安裝 django-simple-captcha(Django 項(xiàng)目) pip install django-simple-captcha
pillow 是 Python 的圖像處理庫,captcha 庫依賴它來生成圖片。如果你用的是 Django,還需要安裝 django-simple-captcha。
方案一:使用 captcha 庫(Flask/FastAPI 通用)
如果你用的是 Flask 或 FastAPI,可以使用 captcha 庫。下面是一個(gè)完整的 Flask 示例:
from flask import Flask, request, session, make_response
from captcha.image import ImageCaptcha
import random
import io
app = Flask(__name__)
app.secret_key = 'your-secret-key'
def generate_captcha():
"""生成驗(yàn)證碼"""
# 生成隨機(jī)驗(yàn)證碼文本(排除易混淆字符)
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
captcha_text = ''.join(random.choices(chars, k=4))
# 創(chuàng)建驗(yàn)證碼圖像
image = ImageCaptcha(width=120, height=40)
data = image.generate(captcha_text)
# 保存驗(yàn)證碼到 session
session['captcha'] = captcha_text
return data
@app.route('/captcha')
def get_captcha():
"""獲取驗(yàn)證碼圖片"""
image_data = generate_captcha()
response = make_response(image_data.getvalue())
response.headers['Content-Type'] = 'image/png'
# 防止緩存,確保每次請(qǐng)求都是新的驗(yàn)證碼
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '0'
return response
@app.route('/verify', methods=['POST'])
def verify_captcha():
"""驗(yàn)證用戶輸入"""
user_input = request.form.get('captcha', '').upper()
server_captcha = session.get('captcha', '')
if user_input == server_captcha:
# 驗(yàn)證成功后清除驗(yàn)證碼,防止重復(fù)使用
session.pop('captcha', None)
return {'success': True, 'message': '驗(yàn)證碼正確'}
else:
return {'success': False, 'message': '驗(yàn)證碼錯(cuò)誤'}, 400
這個(gè)方案的關(guān)鍵點(diǎn):
- 生成驗(yàn)證碼:使用
ImageCaptcha生成圖片,并將驗(yàn)證碼文本保存到 session - 返回圖片:通過 HTTP 響應(yīng)返回圖片,并設(shè)置合適的響應(yīng)頭防止緩存
- 驗(yàn)證輸入:從 session 中讀取驗(yàn)證碼,與用戶輸入進(jìn)行比較
- 安全性:驗(yàn)證成功后清除 session 中的驗(yàn)證碼,防止重復(fù)使用
方案二:Django 項(xiàng)目集成 django-simple-captcha
如果你用的是 Django,使用 django-simple-captcha 會(huì)更方便:
第一步:配置 settings.py
# settings.py
INSTALLED_APPS = [
'captcha',
]
# 驗(yàn)證碼設(shè)置
CAPTCHA_LENGTH = 4 # 字符數(shù)
CAPTCHA_TIMEOUT = 5 # 過期時(shí)間(分鐘)
CAPTCHA_NOISE_FUNCTIONS = ('captcha.helpers.noise_null',)
CAPTCHA_IMAGE_SIZE = (120, 40)
第二步:在 form 中使用
# forms.py
from django import forms
from captcha.fields import CaptchaField
class LoginForm(forms.Form):
username = forms.CharField()
password = forms.CharField(widget=forms.PasswordInput)
captcha = CaptchaField()
第三步:視圖使用
# views.py
from django.shortcuts import render
from .forms import LoginForm
def login_view(request):
if request.method == 'POST':
form = LoginForm(request.POST)
if form.is_valid():
# 驗(yàn)證通過,處理登錄邏輯
username = form.cleaned_data['username']
password = form.cleaned_data['password']
# ... 登錄邏輯
else:
form = LoginForm()
return render(request, 'login.html', {'form': form})
第四步:模板中使用
<!-- login.html -->
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="登錄">
</form>
這個(gè)方案的優(yōu)勢(shì)是集成度非常高,Django 會(huì)自動(dòng)處理驗(yàn)證碼的生成、驗(yàn)證等邏輯,你只需要在表單中添加一個(gè)字段就行。
方案三:高級(jí)自定義驗(yàn)證碼
如果你需要更復(fù)雜的驗(yàn)證碼樣式,可以基于 PIL/Pillow 自己實(shí)現(xiàn):
from captcha.image import ImageCaptcha
from PIL import Image, ImageDraw, ImageFont, ImageFilter
import random
import string
import io
class AdvancedCaptcha:
def __init__(self, width=160, height=60):
self.width = width
self.height = height
self.font_size = 40
def generate_text(self, length=4):
"""生成驗(yàn)證碼文本(排除易混淆字符)"""
chars = string.ascii_uppercase + string.digits
exclude_chars = {'0', 'O', '1', 'I', 'L'}
chars = [c for c in chars if c not in exclude_chars]
return ''.join(random.choices(chars, k=length))
def create_image(self, text):
"""創(chuàng)建驗(yàn)證碼圖像(添加干擾)"""
# 創(chuàng)建畫布
image = Image.new('RGB', (self.width, self.height), (255, 255, 255))
draw = ImageDraw.Draw(image)
# 添加隨機(jī)干擾點(diǎn)
for _ in range(200):
x = random.randint(0, self.width)
y = random.randint(0, self.height)
draw.point((x, y), fill=self._random_color(150, 250))
# 添加隨機(jī)干擾線
for _ in range(5):
x1 = random.randint(0, self.width)
y1 = random.randint(0, self.height)
x2 = random.randint(0, self.width)
y2 = random.randint(0, self.height)
draw.line([(x1, y1), (x2, y2)], fill=self._random_color(100, 200), width=1)
# 繪制文字
try:
font = ImageFont.truetype('arial.ttf', self.font_size)
except:
font = ImageFont.load_default()
# 文字扭曲效果
for i, char in enumerate(text):
# 每個(gè)字符隨機(jī)偏移
x = 20 + i * 35 + random.randint(-5, 5)
y = 5 + random.randint(-5, 5)
draw.text((x, y), char, font=font, fill=self._random_color(20, 120))
# 添加濾鏡效果
image = image.filter(ImageFilter.SMOOTH_MORE)
# 轉(zhuǎn)換為字節(jié)流
img_byte_arr = io.BytesIO()
image.save(img_byte_arr, format='PNG')
img_byte_arr = img_byte_arr.getvalue()
return img_byte_arr, text
def _random_color(self, low, high):
"""生成隨機(jī)顏色"""
return (random.randint(low, high),
random.randint(low, high),
random.randint(low, high))
這個(gè)方案的優(yōu)勢(shì)是可以完全自定義驗(yàn)證碼的樣式,但實(shí)現(xiàn)復(fù)雜度也更高。
最佳實(shí)踐建議
在實(shí)際項(xiàng)目中,除了基本的驗(yàn)證碼功能,我們還需要考慮很多細(xì)節(jié)。
安全性增強(qiáng)
添加過期時(shí)間:驗(yàn)證碼不應(yīng)該永久有效,應(yīng)該設(shè)置過期時(shí)間。比如 5 分鐘后自動(dòng)失效:
import time
from flask import session
def set_captcha_session(text):
session['captcha'] = text
session['captcha_time'] = time.time()
def verify_captcha_with_timeout(user_input, timeout=300): # 5分鐘過期
if 'captcha' not in session or 'captcha_time' not in session:
return False
if time.time() - session['captcha_time'] > timeout:
# 清理過期驗(yàn)證碼
session.pop('captcha', None)
session.pop('captcha_time', None)
return False
return user_input.upper() == session['captcha'].upper()
一次性使用:驗(yàn)證碼應(yīng)該是一次性的,驗(yàn)證成功后立即清除,防止重復(fù)使用。
大小寫不敏感:驗(yàn)證碼驗(yàn)證時(shí)應(yīng)該忽略大小寫,提升用戶體驗(yàn)。
防止重放攻擊:每次驗(yàn)證后都應(yīng)該清除驗(yàn)證碼,防止攻擊者重復(fù)使用同一個(gè)驗(yàn)證碼。
前端集成示例
前端集成驗(yàn)證碼時(shí),需要考慮用戶體驗(yàn):
<!-- HTML前端代碼 -->
<form id="login-form">
<input type="text" name="username" placeholder="用戶名">
<input type="password" name="password" placeholder="密碼">
<div>
<input type="text" name="captcha" placeholder="驗(yàn)證碼">
<img id="captcha-img" src="/captcha"
onclick="this.src='/captcha?'+Date.now()"
style="cursor:pointer; vertical-align:middle;">
<a href="javascript:;" rel="external nofollow" rel="external nofollow" onclick="refreshCaptcha()">換一張</a>
</div>
<button type="submit">登錄</button>
</form>
<script>
function refreshCaptcha() {
// 通過添加時(shí)間戳參數(shù)強(qiáng)制刷新
document.getElementById('captcha-img').src = '/captcha?' + Date.now();
}
</script>
關(guān)鍵點(diǎn):
- 點(diǎn)擊圖片刷新:用戶可以點(diǎn)擊驗(yàn)證碼圖片來刷新
- 換一張鏈接:提供明確的刷新入口
- 防止緩存:通過添加時(shí)間戳參數(shù)防止瀏覽器緩存
生產(chǎn)環(huán)境建議
在生產(chǎn)環(huán)境中,我們還需要考慮更多問題:
頻率限制:對(duì)驗(yàn)證碼請(qǐng)求進(jìn)行 IP 限制,比如 60 秒內(nèi)最多 5 次。這樣可以防止惡意請(qǐng)求:
from flask import request
from functools import wraps
import time
# 簡(jiǎn)單的內(nèi)存緩存(生產(chǎn)環(huán)境建議用 Redis)
request_cache = {}
def rate_limit(max_requests=5, window=60):
def decorator(f):
@wraps(f)
def wrapper(*args, **kwargs):
ip = request.remote_addr
now = time.time()
if ip in request_cache:
requests = [r for r in request_cache[ip] if now - r < window]
if len(requests) >= max_requests:
return {'error': '請(qǐng)求過于頻繁'}, 429
requests.append(now)
request_cache[ip] = requests
else:
request_cache[ip] = [now]
return f(*args, **kwargs)
return wrapper
return decorator
@app.route('/captcha')
@rate_limit(max_requests=5, window=60)
def get_captcha():
# ... 生成驗(yàn)證碼
驗(yàn)證碼多樣性:可以混合使用數(shù)字、字母、算術(shù)驗(yàn)證碼等,增加破解難度。
日志記錄:記錄驗(yàn)證失敗次數(shù),如果某個(gè) IP 連續(xù)失敗多次,可以臨時(shí)封禁。
前后端分離:如果前后端分離,API 可以返回 base64 格式的驗(yàn)證碼:
import base64
@app.route('/api/captcha')
def get_captcha_api():
image_data, text = generate_captcha()
base64_data = base64.b64encode(image_data.getvalue()).decode()
# 保存驗(yàn)證碼到 session 或 Redis
session['captcha'] = text
return {
'image': f'data:image/png;base64,{base64_data}',
'expires_in': 300 # 過期時(shí)間(秒)
}
CDN 緩存:靜態(tài)驗(yàn)證碼圖片可以考慮 CDN 緩存,但要注意防止緩存導(dǎo)致的問題。
商業(yè)方案對(duì)比
對(duì)于對(duì)安全性要求很高的商業(yè)項(xiàng)目,可以考慮使用商業(yè)驗(yàn)證碼方案。下面是幾個(gè)主流方案的對(duì)比:
| 方案 | 優(yōu)點(diǎn) | 缺點(diǎn) | 適用場(chǎng)景 |
|---|---|---|---|
| 自建 captcha | 免費(fèi)、可控、無第三方依賴 | 安全性較低、需自己維護(hù) | 內(nèi)部系統(tǒng)、中小項(xiàng)目 |
| django-simple-captcha | Django 集成好、功能完善 | 僅限 Django、樣式固定 | Django 項(xiàng)目 |
| 極驗(yàn)/騰訊云 | 安全性高、智能驗(yàn)證、防破解 | 收費(fèi)、第三方依賴 | 對(duì)安全性要求高的商業(yè)項(xiàng)目 |
自建方案:適合內(nèi)部系統(tǒng)或中小型項(xiàng)目,成本低但安全性相對(duì)較低。
Django 方案:適合 Django 項(xiàng)目,集成方便但靈活性有限。
商業(yè)方案:適合對(duì)安全性要求很高的商業(yè)項(xiàng)目,安全性高但需要付費(fèi)。
實(shí)際應(yīng)用場(chǎng)景
讓我們看看幾個(gè)實(shí)際應(yīng)用場(chǎng)景,了解如何在不同情況下選擇合適的方案。
場(chǎng)景一:內(nèi)部管理系統(tǒng)
對(duì)于內(nèi)部管理系統(tǒng),對(duì)安全性要求不是特別高,可以選擇簡(jiǎn)單的方案:
# 使用 captcha 庫,簡(jiǎn)單快速
from captcha.image import ImageCaptcha
from flask import Flask, session, make_response
app = Flask(__name__)
app.secret_key = 'your-secret-key'
@app.route('/captcha')
def get_captcha():
image = ImageCaptcha(width=120, height=40)
captcha_text = ''.join(random.choices('0123456789', k=4))
data = image.generate(captcha_text)
session['captcha'] = captcha_text
response = make_response(data.getvalue())
response.headers['Content-Type'] = 'image/png'
return response
這種場(chǎng)景下,簡(jiǎn)單的數(shù)字驗(yàn)證碼就夠用了,用戶體驗(yàn)也比較好。
場(chǎng)景二:用戶注冊(cè)登錄
對(duì)于用戶注冊(cè)登錄功能,需要平衡安全性和用戶體驗(yàn):
# 使用更復(fù)雜的驗(yàn)證碼,但不要太難
class LoginCaptcha:
def generate(self):
# 使用字母+數(shù)字,排除易混淆字符
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
captcha_text = ''.join(random.choices(chars, k=4))
# 生成帶干擾的驗(yàn)證碼圖片
# ...
這種場(chǎng)景下,需要一定的安全性,但也不能太復(fù)雜影響用戶體驗(yàn)。
場(chǎng)景三:API 接口保護(hù)
對(duì)于 API 接口,特別是發(fā)送短信、郵件等會(huì)產(chǎn)生費(fèi)用的接口,需要更強(qiáng)的保護(hù):
- 使用商業(yè)方案或更復(fù)雜的驗(yàn)證碼
- 可以考慮滑動(dòng)驗(yàn)證碼、行為驗(yàn)證碼等
這種場(chǎng)景下,建議使用商業(yè)方案,或者實(shí)現(xiàn)更復(fù)雜的驗(yàn)證碼邏輯。
場(chǎng)景四:前后端分離項(xiàng)目
對(duì)于前后端分離的項(xiàng)目,需要返回 base64 格式的驗(yàn)證碼:
@app.route('/api/captcha')
def get_captcha_api():
image_data, text = generate_captcha()
base64_data = base64.b64encode(image_data).decode()
# 保存到 Redis(推薦)或 session
redis_client.setex(f'captcha:{session_id}', 300, text)
return {
'image': f'data:image/png;base64,{base64_data}',
'expires_in': 300
}
這種場(chǎng)景下,需要考慮驗(yàn)證碼的存儲(chǔ)和驗(yàn)證方式。
總結(jié)
選擇驗(yàn)證碼庫時(shí),需要根據(jù)項(xiàng)目需求來決定:
快速開發(fā):Django 項(xiàng)目用 django-simple-captcha,非 Django 項(xiàng)目用 captcha。
高安全性需求:考慮商業(yè)方案(極驗(yàn)、騰訊云驗(yàn)證碼)。
完全自定義:基于 PIL/Pillow + captcha 自行開發(fā)。
最簡(jiǎn)單的起步方案:
# 最小化實(shí)現(xiàn)
pip install captcha pillow
# 生成驗(yàn)證碼
from captcha.image import ImageCaptcha
image = ImageCaptcha()
data = image.generate('1234')
with open('captcha.png', 'wb') as f:
f.write(data.getvalue())
關(guān)鍵點(diǎn)總結(jié):
- 選擇合適的庫:根據(jù)項(xiàng)目框架和需求選擇合適的庫
- 安全性考慮:添加過期時(shí)間、一次性使用、頻率限制等
- 用戶體驗(yàn):平衡安全性和用戶體驗(yàn),不要過度復(fù)雜
- 生產(chǎn)環(huán)境:考慮日志記錄、頻率限制、前后端分離等
希望這篇文章能幫助你選擇合適的驗(yàn)證碼庫,并在實(shí)際項(xiàng)目中正確使用它們!
完整可運(yùn)行 Demo 代碼
下面是一個(gè)完整的 Flask 示例,展示了如何在實(shí)際項(xiàng)目中使用驗(yàn)證碼:
from flask import Flask, request, session, make_response, render_template_string
from captcha.image import ImageCaptcha
import random
import time
import io
app = Flask(__name__)
app.secret_key = 'your-secret-key-change-in-production'
# HTML 模板
HTML_TEMPLATE = """
<!DOCTYPE html>
<html>
<head>
<title>驗(yàn)證碼示例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 500px;
margin: 50px auto;
padding: 20px;
}
.form-group {
margin-bottom: 15px;
}
label {
display: block;
margin-bottom: 5px;
}
input[type="text"], input[type="password"] {
width: 100%;
padding: 8px;
box-sizing: border-box;
}
.captcha-group {
display: flex;
align-items: center;
gap: 10px;
}
.captcha-img {
cursor: pointer;
border: 1px solid #ddd;
padding: 5px;
}
.refresh-link {
color: #007bff;
text-decoration: none;
}
.refresh-link:hover {
text-decoration: underline;
}
button {
background-color: #007bff;
color: white;
padding: 10px 20px;
border: none;
cursor: pointer;
}
.message {
margin-top: 10px;
padding: 10px;
border-radius: 4px;
}
.success {
background-color: #d4edda;
color: #155724;
}
.error {
background-color: #f8d7da;
color: #721c24;
}
</style>
</head>
<body>
<h2>登錄示例</h2>
<form method="POST" action="/login">
<div class="form-group">
<label>用戶名:</label>
<input type="text" name="username" required>
</div>
<div class="form-group">
<label>密碼:</label>
<input type="password" name="password" required>
</div>
<div class="form-group">
<label>驗(yàn)證碼:</label>
<div class="captcha-group">
<input type="text" name="captcha" required style="flex: 1;">
<img id="captcha-img" class="captcha-img"
src="/captcha"
onclick="refreshCaptcha()"
alt="驗(yàn)證碼">
<a href="javascript:;" rel="external nofollow" rel="external nofollow" onclick="refreshCaptcha()" class="refresh-link">換一張</a>
</div>
</div>
<button type="submit">登錄</button>
</form>
{% if message %}
<div class="message {{ message_type }}">
{{ message }}
</div>
{% endif %}
<script>
function refreshCaptcha() {
document.getElementById('captcha-img').src = '/captcha?' + Date.now();
}
</script>
</body>
</html>
"""
def generate_captcha_text(length=4):
"""生成驗(yàn)證碼文本(排除易混淆字符)"""
chars = 'ABCDEFGHJKLMNPQRSTUVWXYZ23456789'
return ''.join(random.choices(chars, k=length))
def generate_captcha():
"""生成驗(yàn)證碼圖片"""
captcha_text = generate_captcha_text()
# 創(chuàng)建驗(yàn)證碼圖像
image = ImageCaptcha(width=120, height=40)
data = image.generate(captcha_text)
# 保存驗(yàn)證碼到 session(帶時(shí)間戳)
session['captcha'] = captcha_text
session['captcha_time'] = time.time()
return data
@app.route('/')
def index():
"""首頁"""
return render_template_string(HTML_TEMPLATE)
@app.route('/captcha')
def get_captcha():
"""獲取驗(yàn)證碼圖片"""
image_data = generate_captcha()
response = make_response(image_data.getvalue())
response.headers['Content-Type'] = 'image/png'
# 防止緩存
response.headers['Cache-Control'] = 'no-cache, no-store, must-revalidate'
response.headers['Pragma'] = 'no-cache'
response.headers['Expires'] = '0'
return response
def verify_captcha(user_input, timeout=300):
"""驗(yàn)證驗(yàn)證碼(帶過期時(shí)間)"""
if 'captcha' not in session or 'captcha_time' not in session:
return False, '驗(yàn)證碼已過期,請(qǐng)刷新'
# 檢查是否過期(5分鐘)
if time.time() - session['captcha_time'] > timeout:
session.pop('captcha', None)
session.pop('captcha_time', None)
return False, '驗(yàn)證碼已過期,請(qǐng)刷新'
# 驗(yàn)證(忽略大小寫)
if user_input.upper() != session['captcha'].upper():
return False, '驗(yàn)證碼錯(cuò)誤'
# 驗(yàn)證成功后清除驗(yàn)證碼(一次性使用)
session.pop('captcha', None)
session.pop('captcha_time', None)
return True, '驗(yàn)證成功'
@app.route('/login', methods=['GET', 'POST'])
def login():
"""登錄處理"""
if request.method == 'POST':
username = request.form.get('username', '')
password = request.form.get('password', '')
captcha_input = request.form.get('captcha', '')
# 驗(yàn)證驗(yàn)證碼
is_valid, message = verify_captcha(captcha_input)
if not is_valid:
return render_template_string(
HTML_TEMPLATE,
message=message,
message_type='error'
)
# 這里處理實(shí)際的登錄邏輯
# 示例:簡(jiǎn)單的用戶名密碼驗(yàn)證
if username == 'admin' and password == '123456':
return render_template_string(
HTML_TEMPLATE,
message='登錄成功!',
message_type='success'
)
else:
return render_template_string(
HTML_TEMPLATE,
message='用戶名或密碼錯(cuò)誤',
message_type='error'
)
return render_template_string(HTML_TEMPLATE)
if __name__ == '__main__':
app.run(debug=True)
使用說明:
1.安裝依賴:
pip install flask captcha pillow
2.運(yùn)行應(yīng)用:
python app.py
3.訪問應(yīng)用:打開瀏覽器訪問 http://localhost:5000
4.測(cè)試登錄:
- 用戶名:
admin - 密碼:
123456 - 驗(yàn)證碼:輸入圖片中顯示的驗(yàn)證碼
這個(gè) Demo 展示了:
- 驗(yàn)證碼生成:使用
captcha庫生成驗(yàn)證碼圖片 - 驗(yàn)證碼驗(yàn)證:帶過期時(shí)間的一次性驗(yàn)證
- 前端集成:點(diǎn)擊圖片或鏈接刷新驗(yàn)證碼
- 用戶體驗(yàn):清晰的錯(cuò)誤提示和成功提示
你可以根據(jù)實(shí)際需求修改和擴(kuò)展這個(gè)示例。
以上就是Python中三大主流圖片驗(yàn)證碼庫使用推薦與實(shí)踐指南的詳細(xì)內(nèi)容,更多關(guān)于Python圖片驗(yàn)證碼庫的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
- 使用 Python 和 LabelMe 實(shí)現(xiàn)圖片驗(yàn)證碼的自動(dòng)標(biāo)注功能
- 利用Python實(shí)現(xiàn)生成并識(shí)別圖片驗(yàn)證碼
- Python進(jìn)行圖片驗(yàn)證碼識(shí)別方法步驟
- 使用Python生成隨機(jī)圖片驗(yàn)證碼的代碼詳解
- Python中識(shí)別圖片/滑塊驗(yàn)證碼準(zhǔn)確率極高的ddddocr庫詳解
- Python Flask實(shí)現(xiàn)圖片驗(yàn)證碼與郵箱驗(yàn)證碼流程詳細(xì)講解
- python3定位并識(shí)別圖片驗(yàn)證碼實(shí)現(xiàn)自動(dòng)登錄功能
相關(guān)文章
python實(shí)現(xiàn)隨機(jī)森林random forest的原理及方法
本篇文章主要介紹了python實(shí)現(xiàn)隨機(jī)森林random forest的原理及方法,詳細(xì)的介紹了隨機(jī)森林的原理和python實(shí)現(xiàn),非常具有參考價(jià)值,有興趣的可以了解一下2017-12-12
Python3的urllib.parse常用函數(shù)小結(jié)(urlencode,quote,quote_plus,unquot
這篇文章主要介紹了Python3的urllib.parse常用函數(shù),結(jié)合實(shí)例形式分析了urlencode,quote,quote_plus,unquote,unquote_plus等函數(shù)的相關(guān)使用技巧,需要的朋友可以參考下2016-09-09
Python中字符串格式化str.format的詳細(xì)介紹
python從2.6開始支持format,新的更加容易讀懂的字符串格式化方法,下面這篇文章主要介紹了Python中字符串格式化str.format的相關(guān)資料,需要的朋友可以參考借鑒,下面來一起看看吧。2017-02-02
淺談Tensorflow加載Vgg預(yù)訓(xùn)練模型的幾個(gè)注意事項(xiàng)
這篇文章主要介紹了淺談Tensorflow加載Vgg預(yù)訓(xùn)練模型的幾個(gè)注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-05-05
python題解LeetCode303區(qū)域和檢索示例詳解
這篇文章主要為大家介紹了python題解LeetCode303區(qū)域和檢索示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-12-12

