Python生成随机验证码

一、安装依赖

CentOS

第一步:

1
yum install python-devel

第二步:

yum install freetype-devel libjpeg-devel libpng-devel
1
2
sudo yum install libtiff-devel libjpeg-devel libzip-devel freetype-devel \
    lcms2-devel libwebp-devel tcl-devel tk-devel

第三步:RPM包安装PIL

1
2
3
下载安装:ftp://rpmfind.net/linux/atrpms/f20-x86_64/atrpms/stable/PIL-1.1.7-10.1.fc20.x86_64.rpm
 
更多版本:http://rpmfind.net/linux/rpm2html/search.php?query=PIL&submit=Search+...&system=&arch=

Windows

第一步:

1
2
3
4
5
6
7
8
下载安装pip
b. 解压,进入目录
c. 安装,Python setup.py install
 
注:安装过程中可能依赖setuptools,安装过程如下:
下载文件:https://bootstrap.pypa.io/ez_setup.py
执行文件:Python ez_setup.py

第二步:

1
pip3 install Pillow

 

二、使用

Python生成随机验证码,需要使用PIL模块.

1. 创建图片

1
2
3
4
5
6
7
8
9
from PIL import Image
img = Image.new(mode='RGB', size=(12030), color=(255255255))
 
# 在图片查看器中打开
# img.show() 
 
# 保存在本地
with open('code.png','wb') as f:
    img.save(f,format='png')

2. 创建画笔,用于在图片上画任意内容

1
2
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')

3. 画点

1
2
3
4
5
6
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示坐标
# 第二个参数:表示颜色
draw.point([100100], fill="red")
draw.point([300300], fill=(255255255))

4. 画线

1
2
3
4
5
6
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标和结束坐标
# 第二个参数:表示颜色
draw.line((100,100,100,300), fill='red')
draw.line((100,100,300,100), fill=(255255255))

5. 画圆

1
2
3
4
5
6
7
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标和结束坐标(圆要画在其中间)
# 第二个参数:表示开始角度
# 第三个参数:表示结束角度
# 第四个参数:表示颜色
draw.arc((100,100,300,300),0,90,fill="red")

6. 写文本

1
2
3
4
5
6
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
draw.text([0,0],'python',"red")

7. 特殊字体文字

1
2
3
4
5
6
7
8
9
10
img = Image.new(mode='RGB', size=(12030), color=(255255255))
draw = ImageDraw.Draw(img, mode='RGB')
# 第一个参数:表示字体文件路径
# 第二个参数:表示字体大小
font = ImageFont.truetype("kumo.ttf"28)
# 第一个参数:表示起始坐标
# 第二个参数:表示写入内容
# 第三个参数:表示颜色
# 第四个参数:表示颜色
draw.text([00], 'python'"red", font=font)

图片验证码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
import random
 
def check_code(width=120, height=30, char_length=5, font_file='kumo.ttf', font_size=28):
    code = []
    img = Image.new(mode='RGB', size=(width, height), color=(255255255))
    draw = ImageDraw.Draw(img, mode='RGB')
 
    def rndChar():
        """
        生成随机字母   
        :return:
        """
        return chr(random.randint(6590))
 
    def rndColor():
        """
        生成随机颜色
        :return:
        """
        return (random.randint(0255), random.randint(10255), random.randint(64255))
 
    # 写文字
    font = ImageFont.truetype(font_file, font_size)
    for in range(char_length):
        char = rndChar()
        code.append(char)
        = random.randint(04)
        draw.text([i * width / char_length, h], char, font=font, fill=rndColor())
 
    # 写干扰点
    for in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
 
    # 写干扰圆圈
    for in range(40):
        draw.point([random.randint(0, width), random.randint(0, height)], fill=rndColor())
        = random.randint(0, width)
        = random.randint(0, height)
        draw.arc((x, y, x + 4, y + 4), 090, fill=rndColor())
 
    # 画干扰线
    for in range(5):
        x1 = random.randint(0, width)
        y1 = random.randint(0, height)
        x2 = random.randint(0, width)
        y2 = random.randint(0, height)
 
        draw.line((x1, y1, x2, y2), fill=rndColor())
 
    img = img.filter(ImageFilter.EDGE_ENHANCE_MORE)
    return img,''.join(code)
 
 
if __name__ == '__main__':
    # 1. 直接打开
    # img,code = check_code()
    # img.show()
 
    # 2. 写入文件
    # img,code = check_code()
    # with open('code.png','wb') as f:
    #     img.save(f,format='png')
 
    # 3. 写入内存(Python3)
    # from io import BytesIO
    # stream = BytesIO()
    # img.save(stream, 'png')
    # stream.getvalue()
 
    # 4. 写入内存(Python2)
    # import StringIO
    # stream = StringIO.StringIO()
    # img.save(stream, 'png')
    # stream.getvalue()
 
    pass

注意:字体文件下载,猛击这里

 

三、原理

from django.shortcuts import render,HttpResponse
from django.http import JsonResponse
from django.contrib import auth

def login(request):
    # if request.method=="POST":
    if request.is_ajax():
        user=request.POST.get("user")
        pwd=request.POST.get("pwd")
        validcode=request.POST.get("validcode")   #获取用户输入的验证码

        #Ajax请求返回一个字典
        response={"user":None,"err_msg":""}
        if validcode.upper() == request.session.get("keep_str").upper():   #判断验证码是否对
            user_obj=auth.authenticate(username=user,password=pwd)
            if user_obj:
                response["user"]=user
            else:
                response['err_msg']="用户名或者密码错误!"
        else:
            response["err_msg"]="验证码错误!"

        return JsonResponse(response)
    else:
        return render(request, "login.html")


from PIL import Image, ImageDraw, ImageFont
from io import BytesIO
import random

def get_valid_img(request):

    def get_random_color():
        return (random.randint(0,255),random.randint(0,255),random.randint(0,255))

    img = Image.new("RGB", (350, 38), get_random_color())   #新建一个图片对象
    draw = ImageDraw.Draw(img)     #根据图片对象新建一个文本对象
    font = ImageFont.truetype("static/font/kumo.ttf", 32)   #字体对象

    keep_str = ""  #验证码
    for i in range(6):
        random_num = str(random.randint(0, 9))   #随机数字
        random_lowalf = chr(random.randint(97, 122))   #随机小写字母
        random_upperalf = chr(random.randint(65, 90))   #随机大写字母
        random_char = random.choice([random_num,random_lowalf,random_upperalf])
        draw.text((i*30+50,0),random_char, get_random_color(), font=font)   #在图片上写入文本
        keep_str += random_char

    # width = 350
    # height = 38
    # 在验证码图片上随机加10条线
    # for i in range(10):
    #     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())
    #
    # 在验证码图片上随机加50个点
    # for i in range(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())
    # 写与读
    f = BytesIO()   #创建一块内存空间
    img.save(f, "png")    #将图片保存到内存中
    data = f.getvalue()   #读取图片

    # 将验证码存在各自的session中
    request.session['keep_str'] = keep_str

    return HttpResponse(data)
views.py(包含验证码的创建和检验)
<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.css">
</head>
<body>
<h3>登录页面</h3>

<div class="container">
    <div class="row">
        <div class="col-md-8 col-md-offset-2">
            <form action="" method="post">
                {% csrf_token %}
                <div class="form-group">
                    <label for="">用户名</label>
                    <input type="text" class="form-control" id="user">
                </div>
                 <div class="form-group">
                    <label for="">密码</label>
                    <input type="password" class="form-control" id="pwd">
                </div>
                <div class="form-group">
                    <label for="">验证码</label>
                    <div class="row">
                       <div class="col-md-6">
                            <input type="text" class="form-control" id="validcode">
                       </div>
                       <div class="col-md-6">
                           <img width="350" height="38" src="/get_valid_img/" alt="" id="img">
                       </div>
                    </div>

                </div>


                <input type="button" class="btn btn-primary pull-right login_btn" value="登录">
                <span class="error"></span>
            </form>
        </div>
    </div>
</div>


<script src="/static/js/jquery.js"></script>
<script>
     $(".login_btn").click(function () {

         $.ajax({
             url:"",
             type:"post",
             data:{
                 user:$("#user").val(),
                 pwd:$("#pwd").val(),
                 validcode:$("#validcode").val(),
                 csrfmiddlewaretoken:$("[name='csrfmiddlewaretoken']").val()
             },
             success:function (response) {
                     if(response.user){
                         // 登录成功
                         location.href="http://www.luffycity.com"
                     }
                     else{
                         // 登录失败
                         $(".error").html(response.err_msg).css("color","red")
                     }
             }
         })
     })

    //  验证码刷新
    $("#img").click(function () {
        this.src+="?"
    })

</script>
</body>
</html>
login.html(包含ajax请求提交数据和验证码的刷新)

1、当用户访问 http://127.0.0.1:8000/login/ 时,Python自动生成一张图片输入到页面(即:验证码),并将图片上的文字内容保存在 Session中(即:request.session["CheckCode"] = '验证码的文字内容')。

2、用户输入用户名密码,点击登陆时:

  • 首先,检查用户输入的 验证码 是否和Session中保存的验证码相同
  • 然后,检查用户输入的 用户名 和 密码是否正确

使用checkcode示例:

          1、生成验证码图片 <img src='/check_code/'/> session['CheckCode'] = code
          2、看图片,输入图片上的内容
          3、用户输入的值和session['CheckCode']进行比较
          4、用户名和密码的验证

  1 views.py
  2 
  3 import io
  4 import json
  5 from django.shortcuts import HttpResponse, redirect, render
  6 from web.forms.account import SendMsgForm, RegisterForm, LoginForm
  7 from web import models
  8 from backend.utils import check_code as CheckCode
  9 from backend.utils.response import BaseResponse
 10 def check_code(request):
 11     """
 12     获取验证码
 13     :param request:
 14     :return:
 15     """
 16     stream = io.BytesIO()
 17     # 创建随机字符 code
 18     # 创建一张图片格式的字符串,将随机字符串写到图片上
 19     img, code = CheckCode.create_validate_code()
 20     img.save(stream, "PNG")
 21     # 将字符串形式的验证码放在Session中
 22     request.session["CheckCode"] = code
 23     return HttpResponse(stream.getvalue())
 24 def login(request):
 25     """
 26     用户登陆
 27     :param request:
 28     :return:
 29     """
 30     rep = BaseResponse()
 31     form = LoginForm(request.POST)
 32     if form.is_valid():
 33         _value_dict = form.clean()
 34         if _value_dict['code'].lower() != request.session["CheckCode"].lower():
 35             rep.message = {'code': [{'message': '验证码错误'}]}
 36             return HttpResponse(json.dumps(rep.__dict__))
 37         # 验证码正确
 38         from django.db.models import Q
 39 
 40         con = Q()
 41         q1 = Q()
 42         q1.connector = 'AND'
 43         q1.children.append(('email', _value_dict['user']))
 44         q1.children.append(('password', _value_dict['pwd']))
 45 
 46         q2 = Q()
 47         q2.connector = 'AND'
 48         q2.children.append(('username', _value_dict['user']))
 49         q2.children.append(('password', _value_dict['pwd']))
 50 
 51         con.add(q1, 'OR')
 52         con.add(q2, 'OR')
 53 
 54         obj = models.UserInfo.objects.filter(con).first()
 55         if not obj:
 56             rep.message = {'user': [{'message': '用户名邮箱或密码错误'}]}
 57             return HttpResponse(json.dumps(rep.__dict__))
 58 
 59         request.session['is_login'] = True
 60         request.session['user_info'] = {'nid': obj.nid, 'email': obj.email, 'username': obj.username}
 61         rep.status = True
 62     else:
 63         error_msg = form.errors.as_json()
 64         rep.message = json.loads(error_msg)
 65 
 66     return HttpResponse(json.dumps(rep.__dict__))
 67 
 68 
 69 
 70 html
 71 
 72 
 73 <div id="model_login" class="login left">
 74             <div class="header">登陆</div>
 75             <div class="content">
 76                 <div style="padding: 0 70px">
 77                     <div class="tips">
 78                         <span>用户名登陆</span>
 79                         <span style="padding: 0 5px;">|</span>
 80                         <span>邮箱登陆</span>
 81                     </div>
 82                     <div id="login_error_summary" class="error-msg">
 83 
 84                     </div>
 85                     <div class="inp">
 86                         <input name="user" type="text" placeholder="请输入用户名或邮箱" />
 87                     </div>
 88                     <div class="inp">
 89                         <input name="pwd" type="password" placeholder="请输入密码" />
 90                     </div>
 91                     <div class="inp clearfix">
 92                         <input name="code" class="check-code" type="text" placeholder="请输入验证码" />
 93                         <span>
 94                             <img class="check-img" src="/check_code/" alt="验证码" onclick="ChangeCode(this);">
 95                         </span>
 96 
 97                     </div>
 98                     <div class="extra">
 99                         <input type="checkbox" name="autoLogin" checked="checked" /> <span>一个月内自动登录</span>
100                         <a class="right" href="javascript:void(0);">忘记密码?</a>
101                     </div>
102                     <div class="inp">
103                         <div class="submit" onclick="SubmitLogin(this);">
104                             <span>登陆</span>
105                             <span class="hide">
106                                 <img src="/statics/images/loader.gif" style="height: 16px;width: 16px">
107                                 <span>正在登陆</span>
108                             </span>
109                         </div>
110                     </div>
111                 </div>
112                 <script>
113                     function ChangeCode(ths) {
114                         ths.src += '?';
115                     }
116                 </script>
117             </div>
118         </div>
119 
120         /*
121         点击注册按钮
122         */
123         function SubmitRegister(ths){
124             $('#register_error_summary').empty();
125             $('#model_register .inp .error').remove();
126 
127             $(ths).children(':eq(0)').addClass('hide');
128             $(ths).addClass('not-allow').children(':eq(1)').removeClass('hide');
129 
130             var post_dict = {};
131             $('#model_register input').each(function(){
132                 post_dict[$(this).attr("name")] = $(this).val();
133             });
134 
135             $.ajax({
136                 url: '/register/',
137                 type: 'POST',
138                 data: post_dict,
139                 dataType: 'json',
140                 success: function(arg){
141                     if(arg.status){
142                         window.location.href = '/index';
143                     }else{
144                         $.each(arg.message, function(k,v){
145                             //<span class="error">s</span>
146                             var tag = document.createElement('span');
147                             tag.className = 'error';
148                             tag.innerText = v[0]['message'];
149                             $('#model_register input[name="'+ k +'"]').after(tag);
150                         })
151                     }
152                 }
153             });
154 
155             $(ths).removeClass('not-allow').children(':eq(1)').addClass('hide');
156             $(ths).children(':eq(0)').removeClass('hide');
157         }
View Code
posted on 2019-06-15 22:27  始终不够啊  阅读(218)  评论(0编辑  收藏  举报