• python之路_生成验证码及user表继承


    一、创建验证码

      在创建验证码的时候主要用到PIL模块,主要用的方法如下:from PIL import Image,ImageDraw,ImageFont

    1、创建验证码背景图

      为了保证验证码每次的背景色不同,我们将上述color参数通过随机方式获取(颜色由三组在255之内的整数表示),获取颜色的函数如下:

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

    2、添加验证码内容

      验证码应该为区分大小写的字母和数字的随机字符组成,获取这样的随机字符的方式如下:

    def get_random_char():
         random_num=str(random.randint(0,9))
         random_upper_alpha=chr(random.randint(65,90))
         random_lower_alpha=chr(random.randint(97,122))
         random_char=random.choice([random_num,random_upper_alpha,random_lower_alpha])
         return random_char

    3、给验证码加干扰元素

    4、验证码图片保存

      为了前端渲染请求验证码的时候方便,我们需要将验证码图片保存在内存中,需要用到io模块,具体引入方式为:from io import BytesIO,应用如下:

    生成验证码图片的代码如下:

    from PIL import Image,ImageDraw,ImageFont
    import random
    from io import BytesIO
    
    def get_valid_img(request):
        def get_random_color():
            return (random.randint(0,255),random.randint(0,255),random.randint(0,255))
        def get_random_char():
            random_num=str(random.randint(0,9))
            random_upper_alpha=chr(random.randint(65,90))
            random_lower_alpha=chr(random.randint(97,122))
            random_char=random.choice([random_num,random_upper_alpha,random_lower_alpha])
            return random_char
        #生成验证码背景图片
        width=200
        height=35
        image=Image.new(mode="RGB",size=(width,height),color=get_random_color())
        #给验证码背景图片添加验证码内容
        draw=ImageDraw.Draw(image,mode="RGB")
        font=ImageFont.truetype("blog/static/font/kumo.ttf",32)
        valid_code_str=""
        for i in range(1,6):
            char=get_random_char()
            valid_code_str+=char
            draw.text([i*30,3],char,get_random_color(),font=font)
        #给验证码加干扰
        for i in range(80):
            draw.point((random.randint(0,width),random.randint(0,height)),get_random_color())
        for i 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),get_random_color())
        #将生成的整个验证码图片保存到内存
        f=BytesIO()
        image.save(f,"png")
        #从内存中将验证码图片取出来
        data=f.getvalue()
        #将验证码内容存在session,用于登录验证使用
        request.session["valid_code_str"]=valid_code_str
        print(valid_code_str)
        return HttpResponse(data)

    5、前端获取验证码图片

      前端img标签通过src请求生成验证码的函数,便可显示此验证码图片:

    <img src="/get_valid_img/" width="210" height="35" id="valid_img">

      如果验证码看不清,我们需要刷新验证码该如何实现呢?很简单,通过给这个img标签绑定点击事件,重新请求获取验证码图片即可,方式入如下:

    $("#valid_img").click(function () {
           $(this)[0].src+="?"                                               //点击图片后重新请求获取验证码函数
         });

    二、滑动验证码插件

    geetset.py文件:

    import sys
    import random
    import json
    import requests
    import time
    from hashlib import md5
    
    
    if sys.version_info >= (3,):
        xrange = range    
    
    VERSION = "3.0.0"
    
    
    class GeetestLib(object):
    
        FN_CHALLENGE = "geetest_challenge"
        FN_VALIDATE = "geetest_validate"
        FN_SECCODE = "geetest_seccode"
    
        GT_STATUS_SESSION_KEY = "gt_server_status"
    
        API_URL = "http://api.geetest.com"
        REGISTER_HANDLER = "/register.php"
        VALIDATE_HANDLER = "/validate.php"
        JSON_FORMAT = False
    
        def __init__(self, captcha_id, private_key):
            self.private_key = private_key
            self.captcha_id = captcha_id
            self.sdk_version = VERSION
            self._response_str = ""
    
    
        def pre_process(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
            """
            验证初始化预处理.
            //TO DO  arrage the parameter
            """
            status, challenge = self._register(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
            self._response_str = self._make_response_format(status, challenge,new_captcha)
            return status
    
        def _register(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
            pri_responce = self._register_challenge(user_id,new_captcha,JSON_FORMAT,client_type,ip_address)
            if pri_responce:
                if JSON_FORMAT == 1:
                    response_dic = json.loads(pri_responce)
                    challenge = response_dic["challenge"]
                else:
                    challenge = pri_responce
            else:
                challenge=" "
            if len(challenge) == 32:
                challenge = self._md5_encode("".join([challenge, self.private_key]))
                return 1,challenge
            else:
                return 0, self._make_fail_challenge()
    
        def get_response_str(self):
            return self._response_str
    
        def _make_fail_challenge(self):
            rnd1 = random.randint(0, 99)
            rnd2 = random.randint(0, 99)
            md5_str1 = self._md5_encode(str(rnd1))
            md5_str2 = self._md5_encode(str(rnd2))
            challenge = md5_str1 + md5_str2[0:2]
            return challenge
    
        def _make_response_format(self, success=1, challenge=None,new_captcha=1):
            if not challenge:
                challenge = self._make_fail_challenge()
            if new_captcha:
                string_format = json.dumps(
                    {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":True})
            else:
                string_format = json.dumps(
                    {'success': success, 'gt':self.captcha_id, 'challenge': challenge,"new_captcha":False})
            return string_format
    
        def _register_challenge(self, user_id=None,new_captcha=1,JSON_FORMAT=1,client_type="web",ip_address=""):
            if user_id:
                register_url = "{api_url}{handler}?gt={captcha_ID}&user_id={user_id}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                        api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id, user_id=user_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
            else:
                register_url = "{api_url}{handler}?gt={captcha_ID}&json_format={JSON_FORMAT}&client_type={client_type}&ip_address={ip_address}".format(
                        api_url=self.API_URL, handler=self.REGISTER_HANDLER, captcha_ID=self.captcha_id,new_captcha=new_captcha,JSON_FORMAT=JSON_FORMAT,client_type=client_type,ip_address=ip_address)
            try:
                response = requests.get(register_url, timeout=2)
                if response.status_code == requests.codes.ok:
                    res_string = response.text
                else:
                    res_string = ""
            except:
                res_string = ""
            return res_string
    
        def success_validate(self, challenge, validate, seccode, user_id=None,gt=None,data='',userinfo='',JSON_FORMAT=1):
            """
            正常模式的二次验证方式.向geetest server 请求验证结果.
            """
            if not self._check_para(challenge, validate, seccode):
                return 0
            if not self._check_result(challenge, validate):
                return 0
            validate_url = "{api_url}{handler}".format(
                api_url=self.API_URL, handler=self.VALIDATE_HANDLER)
            query = {
                "seccode": seccode,
                "sdk": ''.join( ["python_",self.sdk_version]),
                "user_id": user_id,
                "data":data,
                "timestamp":time.time(),
                "challenge":challenge,
                "userinfo":userinfo,
                "captchaid":gt,
                "json_format":JSON_FORMAT
            }
            backinfo = self._post_values(validate_url, query)
            if JSON_FORMAT == 1:
                backinfo = json.loads(backinfo)
                backinfo = backinfo["seccode"]
            if backinfo == self._md5_encode(seccode):
                return 1
            else:
                return 0
    
        def _post_values(self, apiserver, data):
            response = requests.post(apiserver, data)
            return response.text
    
        def _check_result(self, origin, validate):
            encodeStr = self._md5_encode(self.private_key + "geetest" + origin)
            if validate == encodeStr:
                return True
            else:
                return False
    
        def failback_validate(self, challenge, validate, seccode):
            """
            failback模式的二次验证方式.在本地对轨迹进行简单的判断返回验证结果.
            """
            if not self._check_para(challenge, validate, seccode):
                return 0
            validate_result = self._failback_check_result(
                challenge, validate,)
            return validate_result
    
        def _failback_check_result(self,challenge,validate):
            encodeStr = self._md5_encode(challenge)
            if validate == encodeStr:
                return True
            else:
                return False
    
    
    
        def _check_para(self, challenge, validate, seccode):
            return (bool(challenge.strip()) and bool(validate.strip()) and  bool(seccode.strip()))
    
    
    
        def _md5_encode(self, values):
            if type(values) == str:
                values = values.encode()
            m = md5(values)
            return m.hexdigest()

    view视图函数

    from blog.geetest import GeetestLib
    
    pc_geetest_id = "b46d1900d0a894591916ea94ea91bd2c"
    pc_geetest_key = "36fc3fe98530eea08dfc6ce76e3d24c4"
    
    
    def login2(request):
        return render(request,"login2.html")
    
    def pcgetcaptcha(request):
        user_id = 'test'
        gt = GeetestLib(pc_geetest_id, pc_geetest_key)
        status = gt.pre_process(user_id)
        request.session[gt.GT_STATUS_SESSION_KEY] = status
        request.session["user_id"] = user_id
        response_str = gt.get_response_str()
        return HttpResponse(response_str)
    
    
    def pcajax_validate(request):
        if request.method == "POST":
            gt = GeetestLib(pc_geetest_id, pc_geetest_key)
            challenge = request.POST.get(gt.FN_CHALLENGE, '')
            validate = request.POST.get(gt.FN_VALIDATE, '')
            seccode = request.POST.get(gt.FN_SECCODE, '')
            status = request.session[gt.GT_STATUS_SESSION_KEY]
            user_id = request.session["user_id"]
            username=request.POST.get("username")
            userpswd=request.POST.get("userpswd")
            loginResponse = {"user": None, "error": ""}
            if status:
                result = gt.success_validate(challenge, validate, seccode, user_id)
            else:
                result = gt.failback_validate(challenge, validate, seccode)
            print(result)
            if result:
                user=auth.authenticate(username=username,password=userpswd)
                if user:
                    loginResponse["user"]=user.username
                    auth.login(request,user)
                else:
                    loginResponse["error"]="用户名或者密码错误"
            else:
                loginResponse["error"]="验证码校验错误"
            return JsonResponse(loginResponse)
        return HttpResponse("error")
    login2.html文件:
    <!DOCTYPE html>
    <html lang="zh-CN">
    <head>
        <meta charset="UTF-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no">
        <title>Title</title>
        <link rel="stylesheet" href="/static/bootstrap/css/bootstrap.min.css">
        <!-- 引入封装了failback的接口--initGeetest -->
        <script src="http://static.geetest.com/static/tools/gt.js"></script>
    </head>
    <body>
    <div class="container-fluid " style="background-color: #cccccc;height: 650px">
        <div class="row">
            <div class="col-sm-offset-11">
                <a href="/register/">注册</a>
                <a href="/modify/">修改密码</a>
            </div>
        </div>
        <form class="form-horizontal" style="margin-top: 20px">
            {% csrf_token %}
            <div class="col-sm-offset-4 col-sm-8">
                <h2>滑动验证码登录页面</h2>
            </div>
            <div class="form-group">
                <label for="username" class="col-sm-2 control-label col-sm-offset-2">用户名:</label>
                <div class="col-sm-4">
                    <input type="text" class="form-control" id="username" placeholder="Username">
                </div>
            </div>
            <div class="form-group">
                <label for="userpswd" class="col-sm-2 control-label col-sm-offset-2">密码:</label>
                <div class="col-sm-4">
                    <input type="password" class="form-control" id="userpswd" placeholder="Password">
                    <span class="error" id="login_error"></span>
                </div>
            </div>
            <div class="form-group">
                <div class="col-sm-offset-4 col-sm-4">
                    <input type="button" class="btn btn-success btn-lg btn-block" value="登录" id="login">
                </div>
            </div>
            <div id="popup-captcha"></div>
        </form>
        <div style="margin-left: 220px">
            <img src="/static/3.jpg" alt="" class="img-rounded">
            <img src="/static/3.jpg" alt="" class="img-circle">
            <img src="/static/3.jpg" alt="" class="img-thumbnail">
        </div>
    
    </div>
    <script src="/static/js/jquery-3.2.1.min.js"></script>
    <script src="/static/bootstrap/js/bootstrap.min.js"></script>
    <script>
    
    
        var handlerPopup = function (captchaObj) {
            // 成功的回调
            captchaObj.onSuccess(function () {
                var validate = captchaObj.getValidate();
                $.ajax({
                    url: "/pc-geetest/ajax_validate", // 进行二次验证
                    type: "post",
                    dataType: "json",
                    data: {
                        username: $("#username").val(),
                        userpswd: $("#userpswd").val(),
                        csrfmiddlewaretoken: $('[name="csrfmiddlewaretoken"]').val(),
                        geetest_challenge: validate.geetest_challenge,
                        geetest_validate: validate.geetest_validate,
                        geetest_seccode: validate.geetest_seccode
                    },
                    success: function (data) {
                        if (data.user) {
                            {#                   alert(location.href);#}
                            {#                   alert(location.search);#}
                            if (location.search.slice(6)) {
                                location.href = location.search.slice(6)
                            }
                            else {
                                location.href = "/index/"
                            }
    
                        }
                        else {
                            $("#login_error").text(data.error).css("color", "red");
                            //设置定时器,让错误信息过一段时间后消失
                            setTimeout(function () {
                                $("#login_error").text()
                            }, 3000)
    
                        }
                    }
                });
            });
            $("#login").click(function () {
                captchaObj.show();
            });
            // 将验证码加到id为captcha的元素里
            captchaObj.appendTo("#popup-captcha");
            // 更多接口参考:http://www.geetest.com/install/sections/idx-client-sdk.html
        };
        // 验证开始需要向网站主后台获取id,challenge,success(是否启用failback)
        $.ajax({
            url: "/pc-geetest/register?t=" + (new Date()).getTime(), // 加随机数防止缓存
            type: "get",
            dataType: "json",
            success: function (data) {
                // 使用initGeetest接口
                // 参数1:配置参数
                // 参数2:回调,回调的第一个参数验证码对象,之后可以使用它做appendTo之类的事件
                initGeetest({
                    gt: data.gt,
                    challenge: data.challenge,
                    product: "popup", // 产品形式,包括:float,embed,popup。注意只对PC版验证码有效
                    offline: !data.success // 表示用户后台检测极验服务器是否宕机,一般不需要关注
                    // 更多配置参数请参见:http://www.geetest.com/install/sections/idx-client-sdk.html#config
                }, handlerPopup);
            }
        });
    </script>
    
    </body>
    </html>

    三、登录验证

      我们知道,django为我们提供了auth登录验证模块,会很方便的验证用户是否登陆成功,但是前提是用户名和密码信息是存在django自带的user表,也就是说auth模块是和user表绑定使用的。但是在项目中user表作为用户信息表显然字段不能满足我们的要求。我们的解决的方式当然是自己通过models重新创建用户信息表,但是我们可以通过让我自己创建的表继承user表,这样验证模块同样也就可以使用了。具体继承方式如下:

      通过这样的方式继承user表,必须在setting文件中做如下配置,否则不能完成数据库迁移:

      通过如上的继承以后,我们就可以正常的使用auth模块进行登录验证,如实例:

    def login(request):
        if request.is_ajax():
            username=request.POST.get("username")
            userpswd=request.POST.get("userpswd")
            valid_cod=request.POST.get("valid_code")
            print(valid_cod)
            valid_code_str=request.session.get("valid_code_str")
            print(valid_code_str)
            loginResponse={"user":None,"error":""}
            if valid_code_str.upper()==valid_cod.upper():
                user=auth.authenticate(username=username,password=userpswd)
                if user:
                    auth.login(request,user)
                    loginResponse["user"]=user.username
                else:
                    loginResponse["error"]="username or password error "
            else:
                loginResponse["error"]="valid code error"
            return JsonResponse(loginResponse)                     #可以将字典数据直接序列化,并发回前端,且前端不用反序列化
        return render(request,"login.html")

    四、JsonResponse

      如上实例,我们通过如下方式导入JsonResponse后,我们就能直接将字典类型的数据发回给前端的ajax请求的回调函数,它的作用就是即帮我们做了数据的序列化,又做了数据的反序列化,即后端在发数据时不需要先序列化,前端的回调函数在接收到数据时也不用反序列化。引入方式为:from django.http import JsonResponse

  • 相关阅读:
    easycom HBuilderX 2.5.5起支持easycom组件模式
    我们为什么需要async/await ?
    封装uni.request请求
    uniapp 更新
    uniapp中plus的使用
    uniapp 自适应不同比例的屏幕
    npm 设置淘宝镜像、nrm、nodemon
    uniapp之nvue入坑
    Android平台签名证书(.keystore)生成指南
    day 37 数据库MySQL的进一步认识
  • 原文地址:https://www.cnblogs.com/seven-007/p/8067627.html
Copyright © 2020-2023  润新知