diff --git a/hiboo/captcha/captcha.py b/hiboo/captcha/captcha.py index 2deec4c3d8fa29b14eeb6ffcb5f66e375312f141..8f10be8ee0221916ab120b5a749601a883dff24d 100644 --- a/hiboo/captcha/captcha.py +++ b/hiboo/captcha/captcha.py @@ -2,6 +2,10 @@ from wtforms import widgets from hiboo.captcha import fields import random +import string +import io +import base64 +from PIL import Image, ImageDraw, ImageFont class DummyCaptchaField(fields.CaptchaField): @@ -35,3 +39,57 @@ class DummyCaptchaField(fields.CaptchaField): field.data.isdigit() and challenge == int(field.data) ) + + +class GeneratedTextImageCaptchaField(fields.CaptchaField): + """ Generate a random text and build a fuzzy image based on that text. + + This is the most common historical captcha type, although pretty easily + defeated by OCR nowadays. + """ + + widget = widgets.TextInput() + charset = string.ascii_uppercase + string.digits + + def _value(self): + return "" + + def challenge(self): + self.label = "Please copy the following text: " + length = random.randint(5,8) + return "".join(random.choice(self.charset) for _ in range(length)) + + def check(self, challenge, form, field): + return challenge.lower() == field.data.lower() + + def make_image(self, text): + font = ImageFont.truetype("captcha.ttf", 14) + size = font.getsize(text) + size = (size[0] * 2, int(size[1] * 1.4)) + image = Image.new("RGB", size, "#ffffff") + xpos = 2 + for char in text: + drawn = " {} ".format(char) + fgimage = Image.new("RGB", size, "#000000") + charimage = Image.new("L", font.getsize(drawn), "#000000") + chardraw = ImageDraw.Draw(charimage) + chardraw.text((0, 0), drawn, font=font, fill="#ffffff") + charimage = charimage.rotate(random.randrange(-15,15), expand=0, resample=Image.BICUBIC) + charimage = charimage.crop(charimage.getbbox()) + maskimage = Image.new("L", size) + maskimage.paste(charimage, (xpos, 4, xpos + charimage.size[0], 4 + charimage.size[1])) + size = maskimage.size + image = Image.composite(fgimage, image, maskimage) + xpos = xpos + 2 + charimage.size[0] + image = image.crop((0, 0, xpos + 1, size[1])) + return image + + def __call__(self, **kwargs): + image = self.make_image(self.context["challenge"]) + image_png = io.BytesIO() + image.save(image_png, "PNG") + image_string = base64.b64encode(image_png.getvalue()).decode("ascii") + return widgets.HTMLString( + '<img src="data:image/png;base64,{}">'.format(image_string) + + super(GeneratedTextImageCaptchaField, self).__call__(**kwargs) + ) diff --git a/hiboo/captcha/forms.py b/hiboo/captcha/forms.py index ab1b71e24108b2db8375939364a13dc5cd9ffa66..41aadbbd9b47e068261dd642fd4e506987ca1078 100644 --- a/hiboo/captcha/forms.py +++ b/hiboo/captcha/forms.py @@ -6,5 +6,5 @@ import flask_wtf class DisplayForm(flask_wtf.FlaskForm): test = fields.StringField() - captcha = captcha.DummyCaptchaField() + captcha = captcha.GeneratedTextImageCaptchaField() submit = fields.SubmitField()