最近因為業(yè)務(wù)需求丧靡,需要重寫圖形驗證碼部分。
使用的是Python3.6歉秫,代碼很簡單蛾洛,看一眼基本就知道其中原理,這里僅作記錄雁芙。
from PIL import (
Image, ImageDraw, ImageFont, ImageFilter
)
from django.core.cache import cache
from django.conf import settings
from io import BytesIO
import os, random, string, time
class Captcha:
"""
TODO: 自定義生成圖形驗證碼
using: captcha 獲取圖形驗證碼
using: verify_captcha 驗證圖形驗證碼
"""
def __init__(self):
self._code = string.ascii_uppercase + string.digits
self._width = 100 # 圖片寬
self._height = 40 # 圖片高
self._bits = 4
self._draw_line = True # 干擾線
self._line_num = (1, 5) # 干擾線數(shù)量
self._bgcolor = random.randint(0, 255), random.randint(0, 255), random.randint(0, 255) # 背景顏色
self._font_path = os.path.join(settings.BASE_DIR, 'captcha/fonts/Vera.ttf')
self.captcha_code = self._generate_shuffle_str()
# 生成隨機字符串
def _generate_shuffle_str(self):
shuffle_list = ','.join(self._code).split(',')
random.shuffle(shuffle_list)
return ''.join(shuffle_list[:self._bits])
# 生成圖像
def _generate_image(self):
image = Image.new('RGBA', (self._width, self._height), self._bgcolor) # 畫布
font = ImageFont.truetype(self._font_path, 24) # 用到的字體
draw = ImageDraw.Draw(image) # 畫筆
text = self._generate_shuffle_str()
# 在畫布上畫字著色
for i in range(len(text)):
font_width, font_height = font.getsize(text[i])
draw.text((self._width / self._bits * (i + 1) - font_width,
(self._height - font_height) / random.randint(2, self._bits)),
text[i],
font=font, fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
# 畫上干擾線
if self._draw_line:
self._append_line(draw)
# 畫上躁點
self._append_points(draw)
# 應用圖形變換
image = image.transform((self._width, self._height),
Image.AFFINE,
(1, 0, 0, 0, 1, 0),
Image.BILINEAR) # 創(chuàng)建扭曲
image = image.filter(ImageFilter.EDGE_ENHANCE_MORE) # 漢斯濾鏡轧膘, 邊界加強
return image
# 追加躁點
def _append_points(self, draw):
chance = min(100, max(0, 5))
for w in range(self._width):
for h in range(self._height):
tmp = random.randint(0, 100)
if tmp > 100 - chance:
draw.point((w, h), fill=(0, 0, 0))
# 追加干擾線
def _append_line(self, draw):
for _ in range(random.randint(*self._line_num)):
begin = random.randint(0, self._width), random.randint(0, self._height)
end = random.randint(0, self._width), random.randint(0, self._height)
draw.line([begin, end], fill=(random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)))
def captcha(self):
buf = BytesIO()
im = self._generate_image()
tk = self._cache_captcha()
im.save(buf, 'JPEG')
im.close()
buf.seek(0)
return tk, buf
# 臨時記錄圖形驗證碼
def _cache_captcha(self):
timestamp_key = int(time.time() * 100000000) + random.randint(10, 99)
cache.set(timestamp_key, self.captcha_code, 300)
return timestamp_key
# 驗證圖形驗證碼
def verify_captcha(self, timestamp_key=None, captcha=None):
if timestamp_key and captcha and captcha == cache.get(timestamp_key):
# del the cache data
cache.delete(timestamp_key)
return True
return False
因為我的設(shè)計驗證是key:value形式,所以要把key也傳給移動端却特,這里我使用了下面的方法:
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from .captcha import Captcha
from base64 import b64encode
@csrf_exempt
def get_captcha(request):
captcha = Captcha()
tk, im_buf = captcha.captcha()
# return HttpResponse(im_buf, content_type='image/jpeg')
return JsonResponse({'recode': 1,
'remsg': '獲取成功扶供!',
'data': {'timestamp': tk, 'captcha': b64encode(im_buf.read()).decode('utf-8')}})
注釋的部分用于在瀏覽器里面查看圖片驗證碼。
這里在BytesIO那里踩了點坑裂明,主要還是怪自己學藝不精椿浓。
-- 路漫漫其修遠兮,吾將上下而求索闽晦。