验证码简介
验证码的作用:
验证码在现在来说,是很常见的东西,可以一定程度的保护网站,比如防止网络爬虫恶意爬取网站数据啊,减少低级的攻击啊什么的。但是高级点的骚操作还是不太好防范,所以现在的验证码平台也在升级强化,为了把人和机器严格分开。但是这东西,永远都是看哪一方技术高低的,谁的技术高级,谁就能干倒另一方了。详细的就不说了,总而言之,验证码可以说是目前所有的面向用户的平台都会用到的,所以这项技能必须得会啊。
使用Python库自定义验证码
要自己定义验证码,就得使用pillow库,先用pip install pillow 安装,安装步骤就略过了
这里就直接给一个demo作为讲解,创建一个简单的django项目,项目名为LoginAuth,app名为generic
第一版,图片存在磁盘里
get_validCode_Img就是获取验证码的路由
view:
- get_random_color是获取随机的三个颜色,因为三原色(三基色,具体哪个不深究,这不是重点)就可以组成所有的颜色,所以这里用random随机生成不同的颜色
- get_valid_code_img视图函数就是利用了pillow库的功能
- Image是pillow的画板,ImageDraw是是画笔,ImageFont是字体,我这用的字体是windows电脑自带的arial.ttf,你们也可以去网上下载那种很炫酷的字体放进去,注意路径就行
- ImageDraw.draw.text就是写字的用法,第一个参数是写字的位置,第二个参数就是要写的具体数据
- chr()函数可以将整形数字转为字母,但必须与ASCII码对应,不然容易有问题
- 最后的Image.save就是将刚才的画好的数据保存到一个文件,这个文件当然得是二进制的格式了,然后再读出来返回,这里估计有朋友会想,我塔码的不可以直接把刚才的数据返回吗?不存到图片直接返回,你可以试试,你找找Image对象在没保存之前有没有可以读取它的数据的方法 /滑稽
其他的有关pillow库的具体方法就不多说了,感兴趣自己去研究了
html模板文件,login.html,我引入了bootstrap的cdn,然后用了bt的css样式,在验证码部分,直接用src请求访问,这里有点jsonp的意思
启动项目,打开/login页面,端口绑定的8002
OJBK
第二版,存在内存IO里
但是,就刚才图片那里,如果以后项目上线,供多个账户使用,不同时段都有很多的账户访问,登录平台,那么这个图片我们知道直接就保存在当前目录里:
这样的话就很占空间了,所以我们可以利用IO模块,将这个图片直接存在内存里,不存在磁盘上
只修改视图函数的部分,其他没有做任何改动:
全部代码:
from django.contrib import admin from django.urls import re_path, path from generic.views import login, get_valid_code_img urlpatterns = [ path('admin/', admin.site.urls), path('login/', login), path('get_validCode_img/', get_valid_code_img), ]
# coding:utf-8 from django.shortcuts import render from django.http import HttpResponse, JsonResponse from django.contrib import auth from PIL import Image, ImageDraw, ImageFont import random import io def get_random_color(): return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_valid_code_img(request): # 方式1 : img = Image.new("RGB", (270, 40), color=get_random_color()) draw = ImageDraw.Draw(img) arial_font = ImageFont.truetype("arial.ttf", size=26) valid_code_str = "" for i in range(5): random_num = str(random.randint(0, 9)) random_low_alpha = chr(random.randint(95, 122)) random_upper_alpha = chr(random.randint(65, 90)) random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i * 50 + 20, 5), random_char, get_random_color(), font=arial_font) # 保存验证码字符串 valid_code_str += random_char # with open("validCode.png", "wb") as f: # img.save(f, "png") # with open("validCode.png", "rb") as f: # data = f.read() f = io.BytesIO() img.save(f, "png") data = f.getvalue() return HttpResponse(data) def login(request): if request.method == "POST": response = {"user": None, "msg": None} user = request.POST.get("user") pwd = request.POST.get("pwd") valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str") if valid_code.upper() == valid_code_str.upper(): user = auth.authenticate(username=user, password=pwd) if user: auth.login(request, user) # request.user== 当前登录对象 response["user"] = user.username else: response["msg"] = "用户名或者密码错误!" else: response["msg"] = "验证码错误!" return JsonResponse(response) return render(request, "login.html")
"en"> "UTF-8">登录 "stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">class="container">class="btn btn-danger">用户登录
class="row">class="col-md-4 col-lg-offset-4">
第三版,验证码添加噪点噪线
因为如上的图片是很容易被机器识别的,所以为了增加识别难度,可以添加噪点噪线
其中把验证码存入request.sessoin是为了做比对验证的,这里就不多说了
启动项目:
好的,这就是添加了噪点噪线
代码(其他没变,只改了生成验证码的函数):
# coding:utf-8 from django.shortcuts import render from django.http import HttpResponse, JsonResponse from django.contrib import auth from PIL import Image, ImageDraw, ImageFont import random import io def get_random_color(): return (random.randint(0, 255), random.randint(0, 255), random.randint(0, 255)) def get_valid_code_img(request): # 方式1 : img = Image.new("RGB", (270, 40), color=get_random_color()) draw = ImageDraw.Draw(img) arial_font = ImageFont.truetype("arial.ttf", size=26) valid_code_str = "" for i in range(5): random_num = str(random.randint(0, 9)) random_low_alpha = chr(random.randint(95, 122)) random_upper_alpha = chr(random.randint(65, 90)) random_char = random.choice([random_num, random_low_alpha, random_upper_alpha]) draw.text((i * 30 + 30, 0), random_char, get_random_color(), font=arial_font) # 保存验证码字符串 valid_code_str += random_char width = 170 height = 30 # 噪线 for i in range(3): # 三条线,也可以更多 x1 = random.randint(0, width) x2 = random.randint(0, width) y1 = random.randint(0, height) y2 = random.randint(0, height) draw.line((x1, y1, x2, y2), fill=get_random_color()) # 噪点 for i in range(50): # 50个噪点,也可以更多 draw.point([random.randint(0, width), random.randint(0, height)], fill=get_random_color()) x = random.randint(0, width) y = random.randint(0, height) draw.arc((x, y, x + 4, y + 4), 0, 90, fill=get_random_color()) # 登录验证相关 request.session["valid_code_str"] = valid_code_str request.session['count'] = 0 # with open("validCode.png", "wb") as f: # img.save(f, "png") # with open("validCode.png", "rb") as f: # data = f.read() f = io.BytesIO() img.save(f, "png") data = f.getvalue() return HttpResponse(data) def login(request): if request.method == "POST": response = {"user": None, "msg": None} user = request.POST.get("user") pwd = request.POST.get("pwd") valid_code = request.POST.get("valid_code") valid_code_str = request.session.get("valid_code_str") if valid_code.upper() == valid_code_str.upper(): user = auth.authenticate(username=user, password=pwd) if user: auth.login(request, user) # request.user== 当前登录对象 response["user"] = user.username else: response["msg"] = "用户名或者密码错误!" else: response["msg"] = "验证码错误!" return JsonResponse(response) return render(request, "login.html")
第四版,点击验证码自动刷新
按照电脑常识,应该得有这个功能,点击图片刷新啊,因为看不清啊,就点击刷新,但是如果刷新整个页面的话,表单上已经填好的内容就没了,所以,对了,搞前端的朋友估计更熟一点,用ajax异步请求,只让图片部分刷新就行了,方法是可行的,不过有个更好用的方法,为地址后面加一个【?】即可,对了我下面那个ajax请求的是对用户名和密码的ajax请求验证,并不是对验证码的
启动项目:
一直点,它就一直做jsonp请求并刷新,是不是很方便?
相关代码(跟上面的代码比较只有html文件改了,其他没有变的)
"en"> "UTF-8">登录 "stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous">class="container">class="btn btn-danger">用户登录
class="row">class="col-md-4 col-lg-offset-4">