基于sesson,实现字符图片验证码,点击验证码时刷新,效果如下:

一、验证码生成原理:
1、在html中添加验证码图片,通过src连接至CheckCodeHandler,
2、CheckCodeHandler中生成验证码、带验证码的图片,
3、将验证码写入sesson,
二、验证码使用步骤:
1、form表单提交浏览器输入验证码,
2、取sesson中保存的验证码进行比对,
三、验证码刷新
1、浏览器点击验证码文件,激发js函数,
2、js函数重新发送src请求,即重新刷新验证码图片,
#!/usr/bin/env python # -*- coding:utf-8 -*- import tornado.ioloop import tornado.web container = {} class Sesson: def __init__(self,handler): self.handler = handler self.random_str = None def __generate_random_str(self): # 生成一组随机码(这里使用的是当前时间) import hashlib import time obj = hashlib.md5() obj.update(bytes(str(time.time()),encoding='utf-8')) random_str = obj.hexdigest() return random_str def __setitem__(self, key, value): # 生成随机码对应的容器,保存客户信息,将随机码写入客户端 if not self.random_str: # 一次请求,多次操作时,避免重复刷新随机码 random_str = str(self.handler.get_secure_cookie('kkk'),encoding='utf-8') # 获取客户端随机码 if not random_str: # 如果客户端没有随机码,生成 random_str = self.__generate_random_str() container[random_str] = {} else: if random_str in container.keys(): # 如果客户端有随机码,且与服务器端一致,保留 pass else: # 如果客户端有随机码,但是与服务器端不一致,重新生成 random_str = self.__generate_random_str() container[random_str] = {} self.random_str = random_str container[self.random_str][key] = value self.handler.set_secure_cookie('kkk',self.random_str) def __getitem__(self, key): # 获取客户端的随机码,提取数据 random_str = str(self.handler.get_secure_cookie('kkk'),encoding='utf-8') # 获取客户端随机码 if not random_str: # 如果客户端没有随机码,取不到值,返回空 return None user_info_dict = container.get(random_str,None) # 如果客户端有随机码,但是与服务器端不匹配,也返回空 if not user_info_dict: return None val = user_info_dict.get(key,None) # 如果客户端有随机码,且与服务器端一致,返回相应的值 return val class BaseHandler(tornado.web.RequestHandler): def initialize(self): self.sesson = Sesson(self) class IndexHandler(BaseHandler): def get(self): if self.get_argument('u',None) in ['lucy','luna','dongxue']: self.sesson['is_login'] = True self.sesson['name'] = self.get_argument('u',None) print(container) else: self.write("请注册!") class ManagerHandler(BaseHandler): def get(self): val = self.sesson['is_login'] if val: self.write(self.sesson['name']) else: self.write("失败!") class LoginHandler(BaseHandler): def get(self): self.render('login.html',status = "") def post(self, *args, **kwargs): user = self.get_argument('user') pwd = self.get_argument('pwd') code = self.get_argument('code') check_code = self.sesson["CheckCode"] if code.upper() == check_code.upper(): self.write("验证码正确") else: self.render('login.html',status = "验证码错误") class CheckCodeHandler(BaseHandler): def get(self): import io import check_code mstream = io.BytesIO() # 相当于创建一个内存IO文件,可读写内容 img, code = check_code.create_validate_code() # 创建图片对象img,并写上验证码code, img.save(mstream, "GIF") # 将图片对象写入mstream self.sesson["CheckCode"] = code self.write(mstream.getvalue()) # 路径解析 settings = { "template_path":"views", "static_path":"statics", "cookie_secret":"nifjewoifnewkfcn", } # 二级路由,先匹配域名, application = tornado.web.Application([ (r"/index",IndexHandler), (r"/manager",ManagerHandler), (r"/login",LoginHandler), (r"/check_code",CheckCodeHandler), ],**settings) # 开启服务器,监听 if __name__ == "__main__": application.listen(8888) tornado.ioloop.IOLoop.instance().start()
核心代码check_code.py,其中使用字体文件Monaco.ttf,:
#!/usr/bin/env python #coding:utf-8 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)) 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) 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
文件login.html:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>login</title> </head> <body> <form action="/login" method="post"> <p><input name="user" type="text" placeholder="用户" /></p> <p><input name="pwd" type="text" placeholder="密码" /></p> <p> <input name="code" type="text" placeholder="验证码" /> <img src="/check_code" id="check_code" onclick="ChangeCode()"> </p> <p><input type="submit" value="Submit" /><span style="color:red">{{status}}</span></p> </form> <script type="text/javascript"> function ChangeCode(){ var node = document.getElementById("check_code") node.src += '?' <!--刷新src--> } </script> </body> </html>
浙公网安备 33010602011771号