最近有小伙伴提問:能否說下web驗(yàn)證的原理,感覺文字描述不清楚,于是就用代碼簡(jiǎn)單的演示下:
此代碼是需要依賴:
sanic==19.9.0
Pillow==7.0.0
import random
import string
import uuid
import base64
import platform
from PIL import Image, ImageDraw,ImageFont
from io import BytesIO
from sanic import Sanic
from sanic.response import HTTPResponse,text
from sanic.views import HTTPMethodView
app = Sanic()
session = {}
class VerifyCode:
def __init__(self, numbers:int):
"""
指定:生成的數(shù)量
"""
self.number = numbers
def draw_lines(self, draw, num, width, height):
"""劃線"""
x1 = random.randint(0, width / 2)
y1 = random.randint(0, height / 2)
x2 = random.randint(0, width)
y2 = random.randint(height / 2, height)
draw.line(((x1, y1), (x2, y2)), fill='black', width=1)
def random_color(self):
"""隨機(jī)顏色"""
return (random.randint(32, 127), random.randint(32, 127), random.randint(32, 127))
def gene_text(self):
"""生成驗(yàn)證碼"""
return "".join(random.sample(string.ascii_letters+string.digits, self.number))
def get_verify_code(self):
"""
draw.text():
文字的繪制,第一個(gè)參數(shù)指定繪制的起始點(diǎn)(文本的左上角所在位置),第二個(gè)參數(shù)指定文本內(nèi)容,第三個(gè)參數(shù)指定文本的顏色,第四個(gè)參數(shù)指定字體(通過ImageFont類來定義)
"""
code = self.gene_text()
width, height = 130, 30
im = Image.new("RGB", (width, height), "white")
# 這里指定字體的路徑
sysstr = platform.system()
font = None
if sysstr == "Windows":
font = ImageFont.truetype("C:\WINDOWS\Fonts\STXINGKA.TTF", 25)
elif sysstr == "Darwin":
font = ImageFont.truetype('/Library/Fonts/AppleMyungjo.ttf', 25)
draw = ImageDraw.Draw(im)
for item in range(self.number):
draw.text((5+random.randint(-5,5)+23*item, 5+random.randint(-5, 5)), text=code[item],
fill=self.random_color(), font=font)
self.draw_lines(draw, self.number, width, height)
return im, code
class SimpleView(HTTPMethodView):
body = """
<html>
<head>
<meta charset="UTF-8">
<title>登錄</title>
<link rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">
<!-- 可選的 Bootstrap 主題文件(一般不用引入) -->
<link rel="stylesheet" integrity="sha384-rHyoN1iRsVXV4nD0JutlnGaslCJuC7uwjduW9SVrLvRYooPp2bWYgmgJQIXwl/Sp" crossorigin="anonymous">
<!-- 最新的 Bootstrap 核心 JavaScript 文件 -->
<script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js" integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script>
</head>
<body>
<form class="form-inline" method="post" action="/code">
<div>
<div class="form-group">
{error}
</div>
<div class="form-group">
<label for="exampleInputName2">驗(yàn)證碼</label>
<input type="text" class="form-control" id="captcha" name="code">
</div>
<div class="form-group">
<img src="data:image/jpeg;base64,{base64_data}" class="img-img-rounded">
</div>
<div>
<button type="submit">驗(yàn)證</button>
</div>
</div>
</form>
</body>
</html>
"""
async def get(self, request):
return self.response(error="")
async def post(self, request):
uuid = request.cookies.get("uuid", "1")
verfy_code = request.form.get("code", "2").lower()
code = session.get(uuid, "").lower()
if code == verfy_code:
return text('驗(yàn)證碼正確')
return self.response(error='<input class="form-control" id="disabledInput" type="text" placeholder="驗(yàn)證碼錯(cuò)誤" disabled>')
def response(self, error):
im, code = VerifyCode(5).get_verify_code()
buf = BytesIO()
im.save(buf, "jpeg")
buf_str = buf.getvalue()
base64_data = base64.b64encode(buf_str).decode()
id = uuid.uuid1().__str__()
session[id] = code
body = self.body.format(base64_data=base64_data, error=error)
response = HTTPResponse(body, content_type="text/html; charset=utf-8")
response.cookies["uuid"] = id
return response
app.add_route(SimpleView.as_view(), '/code')
if __name__ == "__main__":
app.run(host="0.0.0.0", port=8000)
只是簡(jiǎn)單的實(shí)現(xiàn)了驗(yàn)證碼,沒有實(shí)現(xiàn)點(diǎn)擊刷新,點(diǎn)擊刷新的原理不難:異步請(qǐng)求+刷新接口就好了,記得更新對(duì)應(yīng)的session的key里面的value