专栏名称: Python学习交流
每天更新,更新python相关的知识。希望诸君有所收获!
目录
相关文章推荐
Python开发者  ·  “李飞飞团队50 美元炼出 ... ·  昨天  
Python爱好者社区  ·  史上最强!PINN杀疯了 ·  昨天  
Python爱好者社区  ·  英伟达憾失DeepSeek关键人才?美国放走 ... ·  昨天  
Python爱好者社区  ·  多模态,杀疯了 ·  2 天前  
Python开发者  ·  请立即拿下软考证书(政策风口) ·  4 天前  
51好读  ›  专栏  ›  Python学习交流

Django实现验证码

Python学习交流  · 公众号  · Python  · 2017-10-14 15:29

正文

Django实现验证码

背景知识

1. 验证码的作用

  • 防恶意破解密码:防止,使用程序或机器人恶意去试密码.为了提高用户的体验,用户输入错误以后,才会要求输入验证码.

  • 防论坛灌水:这个是很常见的。有一种程序叫做顶帖机,如果无限制的刷,整个论坛可能到处是拉圾信息,比如,百度贴吧 ,你只要是新用户或者刚刚关注的贴吧,要是发帖,会马上出现验证码。

  • 有效防止注册,以防,使用程序或机器人去无限制注册账号.

  • 防刷票,网上有很多投票类的网站.

2. 验证码的原理

验证码于服务器端生成,发送给客户端,并以图像格式显示。客户端提交所显示的验证码,客户端接收并进行比较,若比对失败则不能实现登录或注册,反之成功后跳转相应界面


代码实现

# -*- coding:utf-8 -*-

from PIL import Image, ImageDraw, ImageFont

import random, StringIO

import os

from math import ceil

import base64


current_path = os.path.normpath(os.path.dirname(__file__))



class Captcha(object):

# 定义一个验证码类,

def __init__(self, request):

self.django_request = request

self.session_key = request.session.session_key

self.words = []


# image size (pix)

self.img_width = 150

self.img_height = 30


# default type

self.type = 'number'


def _get_font_size(self):

"""  将图片高度的80%作为字体大小

"""

s1 = int(self.img_height * 0.8)

s2 = int(self.img_width / len(self.code))

return int(min((s1, s2)) + max((s1, s2)) * 0.05)


def _get_words(self):

""" The words list

"""

# 扩充单词列表

if self.words:

return set(self.words)


file_path = os.path.join(current_path, 'words.list')

f = open(file_path, 'r')

return set([line.replace('\n', '') for line in f.readlines()])


def _set_answer(self, answer):

"""  设置答案

"""

self.django_request.session[self.session_key] = str(answer)


def _yield_code(self):

"""  生成验证码数字,以及答案

"""

# 数字公式验证码

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.type.lower())

return fun()


def display(self):

"""  把生成的验证码图片改成数据流返回

"""


# 字体颜色

self.font_color = ['black', 'darkblue', 'darkred']


# 背景颜色,随机生成

self.background = (random.randrange(230, 255), random.randrange(230, 255), random.randrange(230, 255))


# 字体

self.font_path = os.path.join(current_path, 'timesbi.ttf')

# self.font_path = os.path.join(current_path,'Menlo.ttc')


# 生成的验证码只做一次验证,就会清空

self.django_request.session[self.session_key] = ''


# 使用 PIL创建画布

im = Image.new('RGB', (self.img_width, self.img_height), self.background)


# 生成验证码

self.code = self._yield_code()


# 设置字体大小

self.font_size = self._get_font_size()


# 实例化一个绘图

draw = ImageDraw.Draw(im)


# 在画布绘图,写验证码

if self.type == 'word':

c = int(8 / len(self.code) * 3) or 3

elif self.type == '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(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))

# draw.arc(xy,fill=line_color,width=int(self.font_size*0.1))

# draw.arc(xy,0,1400,fill=line_color)

# code part

j = int(self.font_size * 0.3)

k = int(self.font_size * 0.5)

x = random.randrange(j, k)  # starts point

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

# 序列化处理

buf = StringIO.StringIO()

im.save(buf, 'gif')

buf.closed

data = base64.encodestring(buf.getvalue())

return data


def validate(self, code):

"""

检查用户输入和服务器上的密码是否一致

"""

if not code:

return False

_code = self.django_request.session.get(self.session_key) or ''

self.django_request.session[self.session_key] = ''

return _code.lower() == str(code).lower()


def check(self, code):

"""

检查用户输入和服务器上保存的密码是否一致

"""

return self.validate(code)



上面使用的库如下:

from PIL import Image, ImageDraw, ImageFont
    import random, StringIO
    import os
    from math import ceil
    import base64

说明:

  • PIL 画图,生成图片

  • random 随机生成数 math用于计算

  • StringIO将图片格式转成数据流用于网络传输

  • base64,用户编码,数据传输,应前端要求处理跨域API的问题

需要强调的是:







请到「今天看啥」查看全文