换回之前的capacha代码

This commit is contained in:
zemal 2017-08-20 20:41:48 +08:00
parent 07643e2639
commit 99fd87dbcf
2 changed files with 61 additions and 113 deletions

View File

@ -237,7 +237,7 @@ class UserRegisterAPI(APIView):
"""
data = request.data
captcha = Captcha(request)
if not captcha.validate(data["captcha"]):
if not captcha.check(data["captcha"]):
return self.error("Invalid captcha")
if User.objects.filter(username=data["username"]).exists():
return self.error("Username already exists")
@ -260,7 +260,7 @@ class UserChangePasswordAPI(APIView):
"""
data = request.data
captcha = Captcha(request)
if not captcha.validate(data["captcha"]):
if not captcha.check(data["captcha"]):
return self.error("Invalid captcha")
username = request.user.username
user = auth.authenticate(username=username, password=data["old_password"])

View File

@ -1,5 +1,5 @@
"""
Copyright 2017 TY<master@t-y.me>
Copyright 2013 TY<tianyu0915@gmail.com>
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
@ -12,145 +12,93 @@ limitations under the License.
"""
import os
import time
import random
from math import ceil
from six import BytesIO
from PIL import Image, ImageDraw, ImageFont
__version__ = "0.3.3"
current_path = os.path.normpath(os.path.dirname(__file__))
from io import BytesIO
from PIL import Image, ImageDraw, ImageFont
class Captcha(object):
def __init__(self, request):
""" something init
"""
初始化,设置各种属性
"""
self.django_request = request
self.session_key = "_django_captcha_key"
self.words = ["hello", "word"]
self.captcha_expires_time = "_django_captcha_expires_time"
# image size (pix)
self.img_width = 150
# 验证码图片尺寸
self.img_width = 90
self.img_height = 30
self.type = "number"
self.mode = "number"
def _get_font_size(self):
def _get_font_size(self, code):
"""
将图片高度的80%作为字体大小
"""
s1 = int(self.img_height * 0.8)
s2 = int(self.img_width / len(self.code))
s2 = int(self.img_width / len(code))
return int(min((s1, s2)) + max((s1, s2)) * 0.05)
def _get_words(self):
""" words list
"""
# TODO 扩充单词表
if self.words:
return set(self.words)
def _set_answer(self, answer):
"""
设置答案和过期时间
"""
self.django_request.session[self.session_key] = str(answer)
self.django_request.session[self.captcha_expires_time] = time.time() + 60
def _generate(self):
# 英文单词验证码
def word():
code = random.sample(self._get_words(), 1)[0]
self._set_answer(code)
return code
# 数字公式验证码
def number():
m, n = 1, 50
x = random.randrange(m, n)
y = random.randrange(m, n)
r = random.randrange(0, 2)
if r == 0:
code = "%s - %s = ?" % (x, y)
z = x - y
else:
code = "%s + %s = ?" % (x, y)
z = x + y
self._set_answer(z)
return code
fun = eval(self.mode.lower())
return fun()
def _make_code(self):
"""
生成随机数或随机字符串
"""
string = random.sample("abcdefghkmnpqrstuvwxyzABCDEFGHGKMNOPQRSTUVWXYZ23456789", 4)
self._set_answer("".join(string))
return string
def get(self):
""" return captcha image bytes
"""
生成验证码图片,返回值为图片的bytes
"""
background = (random.randrange(200, 255), random.randrange(200, 255), random.randrange(200, 255))
code_color = (random.randrange(0, 50), random.randrange(0, 50), random.randrange(0, 50), 255)
# font color
self.font_color = ["black", "darkblue", "darkred"]
font_path = os.path.join(os.path.normpath(os.path.dirname(__file__)), "timesbi.ttf")
# background color
self.background = (random.randrange(230, 255), random.randrange(230, 255), random.randrange(230, 255))
image = Image.new("RGB", (self.img_width, self.img_height), background)
code = self._make_code()
font_size = self._get_font_size(code)
draw = ImageDraw.Draw(image)
# font path
self.font_path = os.path.join(current_path, "timesbi.ttf") # or Menlo.ttc
# x是第一个字母的x坐标
x = random.randrange(int(font_size * 0.3), int(font_size * 0.5))
self.django_request.session[self.session_key] = ""
im = Image.new("RGB", (self.img_width, self.img_height), self.background)
self.code = self._generate()
for i in code:
# 字符y坐标
y = random.randrange(1, 7)
# 随机字符大小
font = ImageFont.truetype(font_path.replace("\\", "/"), font_size + random.randrange(-3, 7))
draw.text((x, y), i, font=font, fill=code_color)
# 随机化字符之间的距离 字符粘连可以降低识别率
x += font_size * random.randrange(6, 8) / 10
# set font size automaticly
self.font_size = self._get_font_size()
# creat
draw = ImageDraw.Draw(im)
# draw noisy point/line
if self.mode == "word":
c = int(8 / len(self.code) * 3) or 3
elif self.mode == "number":
c = 4
for i in range(random.randrange(c - 2, c)):
line_color = (random.randrange(0, 255), random.randrange(0, 255), random.randrange(0, 255))
xy = (random.randrange(0, int(self.img_width * 0.2)), random.randrange(0, self.img_height),
random.randrange(int(3 * self.img_width / 4), self.img_width), random.randrange(0, self.img_height))
draw.line(xy, fill=line_color, width=int(self.font_size * 0.1))
# main part
j = int(self.font_size * 0.3)
k = int(self.font_size * 0.5)
x = random.randrange(j, k)
for i in self.code:
# 上下抖动量,字数越多,上下抖动越大
m = int(len(self.code))
y = random.randrange(1, 3)
if i in ("+", "=", "?"):
# 对计算符号等特殊字符放大处理
m = ceil(self.font_size * 0.8)
else:
# 字体大小变化量,字数越少,字体大小变化越多
m = random.randrange(0, int(45 / self.font_size) + int(self.font_size / 5))
self.font = ImageFont.truetype(self.font_path.replace("\\", "/"), self.font_size + int(ceil(m)))
draw.text((x, y), i, font=self.font, fill=random.choice(self.font_color))
x += self.font_size * 0.9
del x
del draw
self.django_request.session[self.session_key] = "".join(code)
with BytesIO() as buf:
im.save(buf, "gif")
image.save(buf, "gif")
buf_str = buf.getvalue()
return buf_str
def validate(self, code):
""" user input validate
def check(self, code):
"""
检查用户输入的验证码是否正确
"""
if not code:
return False
code = code.strip()
_code = self.django_request.session.get(self.session_key) or ""
self.django_request.session[self.session_key] = ""
return _code.lower() == str(code).lower()
if not _code:
return False
expires_time = self.django_request.session.get(self.captcha_expires_time) or 0
# 注意 如果验证之后不清除之前的验证码的话 可能会造成重复验证的现象
del self.django_request.session[self.session_key]
del self.django_request.session[self.captcha_expires_time]
if _code.lower() == str(code).lower() and time.time() < expires_time:
return True
else:
return False