check_code.py(需要字体文件:Monaco.ttf)
import random
from PIL import Image, ImageDraw, ImageFont, ImageFilter
_letter_cases = "abcdefghjkmnpqrstuvwxy" # 小写字母,去除可能干扰的i,l,o,z
_upper_cases = _letter_cases.upper() # 大写字母
_numbers = ''.join(map(str, range(3, 10))) # 数字
init_chars = ''.join((_letter_cases, _upper_cases, _numbers))
# PIL
def create_validate_code(size=(120, 30),
chars=init_chars,
img_type="GIF",
mode="RGB",
bg_color=(255, 255, 255),
fg_color=(0, 0, 255),
font_size=18,
font_type="Monaco.ttf",
length=4,
draw_lines=True,
n_line=(1, 2),
draw_points=True,
point_chance=2):
"""
@todo: 生成验证码图片
@param size: 图片的大小,格式(宽,高),默认为(120, 30)
@param chars: 允许的字符集合,格式字符串
@param img_type: 图片保存的格式,默认为GIF,可选的为GIF,JPEG,TIFF,PNG
@param mode: 图片模式,默认为RGB
@param bg_color: 背景颜色,默认为白色
@param fg_color: 前景色,验证码字符颜色,默认为蓝色#0000FF
@param font_size: 验证码字体大小
@param font_type: 验证码字体,默认为 ae_AlArabiya.ttf
@param length: 验证码字符个数
@param draw_lines: 是否划干扰线
@param n_lines: 干扰线的条数范围,格式元组,默认为(1, 2),只有draw_lines为True时有效
@param draw_points: 是否画干扰点
@param point_chance: 干扰点出现的概率,大小范围[0, 100]
@return: [0]: PIL Image实例
@return: [1]: 验证码图片中的字符串
"""
width, height = size # 宽高
# 创建图形
img = Image.new(mode, size, bg_color)
draw = ImageDraw.Draw(img) # 创建画笔
def get_chars():
"""生成给定长度的字符串,返回列表格式"""
return random.sample(chars, length)
def create_lines():
"""绘制干扰线"""
line_num = random.randint(*n_line) # 干扰线条数
for i in range(line_num):
# 起始点
begin = (random.randint(0, size[0]), random.randint(0, size[1]))
# 结束点
end = (random.randint(0, size[0]), random.randint(0, size[1]))
draw.line([begin, end], fill=(0, 0, 0))
def create_points():
"""绘制干扰点"""
chance = min(100, max(0, int(point_chance))) # 大小限制在[0, 100]
for w in range(width):
for h in range(height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
def create_strs():
"""绘制验证码字符"""
c_chars = get_chars()
strs = ' %s ' % ' '.join(c_chars) # 每个字符前后以空格隔开
font = ImageFont.truetype(font_type, font_size)
print(dir(font))
print(type(font))
box = font.getbbox(strs)
font_width = box[2]-box[0]
font_height = box[3]-box[1]
# font_width, font_height = font.getsize(strs)
draw.text(((width - font_width) / 3, (height - font_height) / 3),
strs, font=font, fill=fg_color)
return ''.join(c_chars)
if draw_lines:
create_lines()
if draw_points:
create_points()
strs = create_strs()
# 图形扭曲参数
params = [1 - float(random.randint(1, 2)) / 100,
0,
0,
0,
1 - float(random.randint(1, 10)) / 100,
float(random.randint(1, 2)) / 500,
0.001,
float(random.randint(1, 2)) / 500
]
img = img.transform(size, Image.PERSPECTIVE, params) # 创建扭曲
img = img.filter(ImageFilter.EDGE_ENHANCE_MORE) # 滤镜,边界加强(阈值更大)
return img, strs
LoginForm
class LoginForm(BaseForm, forms.Form): // 继承一个基类:BaseForm
username = fields.CharField(max_length=16, label='用户名')
password = fields.Field(
validators=(RegexValidator(regex='^.*(?=.{8,})(?=.*\d)(?=.*[a-zA-Z])(?=.*[!@#$%^&*? ]).*$',
message="密码(长度8-16位)需包含数字、字母和特殊字符"),
validators.MaxLengthValidator(16)
),
widget=widgets.PasswordInput(),
label='密码',
)
check_code = fields.CharField(max_length=4, label='验证码')
def clean_check_code(self):
code = self.cleaned_data['check_code']
cc = self.request.session.get('check_code')
if cc == None: # 不知道什么情况会这样,但还是写着
raise ValidationError('验证码已过期')
if code.upper() != cc.upper():
raise ValidationError('验证码不正确')
return self.cleaned_data #教程中直接pass,不用return
#基类定义如下:
class BaseForm():
def __init__(self, request, *args, **kwargs):
self.request=request
super().__init__(*args, **kwargs)
HTML中加入验证码
<input type="text" id="inputCode" class="form-control" placeholder="验证码" name="check_code" required autofocus>
<img src="/check_code/" class="check-code" id="checkcode">
点击验证码图片刷新验证码
function bindCheckcode() {
$('#checkcode').click(function () {
var check_code_img = $('#checkcode')[0];
check_code_img.src = check_code_img.src + '?';/// 加?,可以重新请求数据
});
}
# 登录请求失败,同样刷新验证码
验证码生成
#urls.py中:
path('check_code/', views.check_code)
#views.py中
def check_code(request):
stream = BytesIO() #与获取文件句柄对应,这是获取内存中的句柄
img,code = check_code_utils.create_validate_code() #生成验证码,返回png图片和验证码字符串
img.save(stream,'PNG') #将图片保存到内存
request.session['check_code'] = code #将验证码字符串保存到session
return HttpResponse(stream.getvalue())
标签:code,img,验证码,param,简单,三十,font,check
From: https://www.cnblogs.com/zhlforhe/p/18014017