La création d'un CAPTCHA arithmétique consiste à afficher une opération mathématique sous forme d'image et à demander à l'utilisateur de saisir le résultat. Cette approche repose sur la bibliothèque Pillow pour le rendu visuel et l'encodage base64 pour la transmission.
Un fichier de police au format .ttf est nécessaire pour le rendu du texte sur l'image.
Module de génération
La classe ci-dessous encapsule trois responsabilités distinctes : la création d'équations aléatoires, le rendu en image base64, et le décodage du résultat pour vérification.
import io
import random
import base64
from PIL import Image, ImageDraw, ImageFont
class GenerateurCaptchaArithmetique:
"""Génère des CAPTCHA basés sur des opérations mathématiques élémentaires."""
CALCULS = {
'+': lambda a, b: a + b,
'-': lambda a, b: a - b,
'x': lambda a, b: a * b,
'/': lambda a, b: a // b
}
def creer_equation(self):
"""Produit une équation aléatoire avec sa solution."""
n1 = random.randint(1, 20)
n2 = random.randint(1, 20)
operateur = random.choice(list(self.CALCULS.keys()))
# Garantit un résultat entier pour la division
if operateur == '/' and n2 != 0:
n1 = n1 * n2
texte = f"{n1} {operateur} {n2}"
resultat = self.CALCULS[operateur](n1, n2)
return texte, resultat
def dessiner_image(self, texte):
"""Génère une image PNG encodée en base64 à partir du texte fourni."""
largeur = 130
hauteur = 50
fond = (190, 205, 220)
encre = (20, 20, 20)
img = Image.new("RGB", (largeur, hauteur), fond)
stylo = ImageDraw.Draw(img)
typo = ImageFont.truetype("chemin_vers_police.ttf", 38)
cadre = stylo.textbbox((0, 0), texte, font=typo)
t_largeur = cadre[2] - cadre[0]
t_hauteur = cadre[3] - cadre[1]
origine_x = (largeur - t_largeur) // 2
origine_y = (hauteur - t_hauteur) // 2
stylo.text((origine_x, origine_y), texte, font=typo, fill=encre)
memoire = io.BytesIO()
img.save(memoire, format="PNG")
encode = base64.b64encode(memoire.getvalue()).decode("utf-8")
return f"data:image/png;base64,{encode}"
def extraire_resultat(self, texte):
"""Décode et calcule le résultat d'une équation affichée."""
parties = texte.split()
gauche = int(parties[0])
op = parties[1]
droite = int(parties[2])
return self.CALCULS[op](gauche, droite)
Intégration dans une vue Django REST
L'endpoint suivant expose le CAPTCHA via une requête GET. Il renvoie l'image encodée ainsi que la solution attendue, que le frontend peut stocker temporairement pour valider la réponse de l'utilisateur.
from rest_framework.views import APIView
from rest_framework.response import Response
class CaptchaEndpoint(APIView):
def get(self, requete):
generateur = GenerateurCaptchaArithmetique()
equation, solution = generateur.creer_equation()
image_base64 = generateur.dessiner_image(equation)
return Response({
"statut": 200,
"message": "succès",
"image": image_base64,
"reponse": solution
})
L'image générée affiche une équation du type 15 x 3 ou 8 + 12 centrée sur un fond coloré, prête à être intégrée dans une balise <img> via l'attribut src avec le préfixe data:image/png;base64,.