Flask项目之手机端租房网站的实战开发(六)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_41782425/article/details/85934776

说明:该篇博客是博主一字一码编写的,实属不易,请尊重原创,谢谢大家!

接着上一篇博客继续往下写 :https://blog.csdn.net/qq_41782425/article/details/85858348

目录

一丶补充

二丶短信验证码前端编写 

三丶注册后端接口编写

四丶注册后端测试

五丶注册前端编写

六丶用户注册功能测试


一丶补充

1.之前写的获取的图片验证码,有一个地方需要做进一步的处理,需要删除redis数据库中的图片验证码,防止用户使用同一个图片验证码验证多次,所以将删除代码放在取出redis数据库中图片验证码的值之后,也就是在real_image_code变量之后进行删除,这样即使删除了redis数据中的数据,也不会影响接下来的判断校验,这样做的目的就是说不管用户填对填错,只有一次验证机会

2.测试以上逻辑是否正确

  • step2 查看redis数据库图片验证码的值

  • step3 在Postman工具中发送短信验证码路由地址

此时绑定的手机号码上就会收到我们定义的6位数验证码

  • step4 查看redis数据库(这个时候并没有超过180秒),发现图片验证码的key已经被删除

3.短信验证码这一块,大多网站都会在用户发送第一次验证码后,在前端页面会出现60s后再试之类的提醒,那么对于后端来说,也是需要的,比如被人知道了后我端口的接口,1分钟之类不断的发送短信,那可就不好了,所以为了做到优化以及安全防范,那么就需要在后端进行一个禁止60s内发送第二次短信的一个逻辑,不仅仅限于前端

  • step1 逻辑分析,首先当发送第一次短信时,需要记住发送短信记录,当时间超过60s以上,才可以发送第二条短信,此时就需要想想是在什么时候将这个发送短信的记录存到redis数据库,大多公司是这样做的,不管发送成功与否,都是需要等待60s后才能发送第二次,所以我们这里也是这样做的,在发送短信之前就将记录存到redis数据库当中去
  • step2 当在保存短信验证码的时候,将这个第一次发送短信的记录存到redis数据库中,让redis数据库去维护记录的有效期60s,这个记录的数据随便起,这里设置1,那么当在redis数据库查询不到这个记录时,代表可以记录过了有效期被删除了,则代表可以发送第二条短信验证码了
redis_store.setex("send_sms_code_%s" % mobile, constants.SEND_SMS_CODE_INTERVAL, 1)
  • step3 然后在判断手机号是否注册之前进行校验判断,判断对于此手机号码在60s以内有没有之前发送短信的操作,如果有,则不代表用户操作频繁,不予处理(注:经测试如果在判断获取记录的值的时候代码为if send_flag == 1时,会出现bug,所以建议使用is not None即可)

4.测试以上逻辑是否正常

  • step1 启动项目程序

  • step2 刷新http://127.0.0.1:5000/register.html 注册页面,生成新的图片验证码,将验证码内容以及对应编码通过发送短信路由发送到对应接口进行测试

  • step3 此时从redis数据库中查看图片验证码的id以及值

  • step4 在Postman工具中,对短信接口地址进行访问,并携带image_code以及image_code_id

同时收到手机短信验证码

  • step5 最关键的一步,与此同时,刷新图片验证码,获取新的图片验证码,然后在redis数据查询id以及图片验证码的值后在Postman中进行再次发送(注:这里的演示是在60s之内发送第二次请求给后端短信接口),出现如下提示,代表逻辑正确

二丶短信验证码前端编写 

1.在register.js中构造请求数据

var req_data = {
      "image_code": imageCode,  //用户输入图片验证码的值
      "image_code_id": imageCodeId  //对应图片验证码的编号
    };

2.通过ajax方式想后端去发送请求

// 想后端发送请求
$.get("/api/v1.0/sms_codes/"+mobile, req_data, function (resp) {
    // 回调函数中的resp是后端返回的json字符串,通过ajax将这个字符串转换成js对象
    // 所以这里的resp为ajax转换后的对象
    if (resp.error == "0") {
        // 表示发送成功
        var num = 60;
        var timer = setInterval(function () {
            if (num>1) {
                // 修改倒计时的文本内容
                $(".phonecode-a").html(num + "s");
                num -= 1
            } else {
                $(".phonecode-a").html("获取验证码");
                $(".phonecode-a").attr("onclick", "sendSMSCode();");
                clearInterval(timer)
            }
        }, 1000, 60)
    } else {
        alert(resp.errmsg);
        $(".phonecode-a").attr("onclick", "sendSMSCode();");
    }

});

3.测试

  • step1 启动程序

  • step3 输入手机号,图片验证码后点击获取短信验证码,成功出现倒计时,同时手机收到短信验证码

手机验证码

  • step4 在谷歌浏览器中查看接口地址的NetWork

General数据

Response响应数据 

三丶注册后端接口编写

1.在项目api_1_0目录下创建passport.py文件用作于注册以及登录的逻辑接口文件

2.构建register视图函数,并在ihome/init文件中导入passport模块

@api.route("/users", methods=["POST"])
def register():
    pass

3.逻辑编写

  • step1 获取前端请求发送请求中的json数据
req_dict = request.get_json()
mobile = req_dict.get("mobile") #跟前端约定好的
sms_code = req_dict.get("sms_code")
password = req_dict.get("password")
password2 = req_dict.get("password2")
  • step2 校验参数
if not all([mobile, sms_code, password, password2]):
    return jsonify(errno=RET.PARAMERR, errmsg="请求参数不完整")
  • step3 判断用户输入的手机号码格式

  • step4 从redis数据库中取出短信验证码
try:
    real_sms_code = redis_store.get("sms_code_%s" % mobile)
except Exception as e:
    current_app.logger.error(e)
    return jsonify(errno=RET.DBERR, errmsg="读取真实短信验证码异常")
  • step5 判断短信验证码是否过期
if real_sms_code is None:
    return jsonify(errno=RET.NODATA, errmsg="短信验证码失效")
  • step6 判断用户输入的短信验证码是否正确
if real_sms_code != sms_code:
    return jsonify(errno=RET.DATAERR, errmsg="短信验证码填写错误")
  • step7 同图片验证码一样,在redis数据库中进行删除,防止一个短信验证码多次使用
try:
    redis_store.delete("sms_code_%s" % mobile)
except Exception as e:
    current_app.logger.error(e)
  • step8 判断用户的手机号是否注册过

  • step9 将用户注册的数据保存到mysql数据库中,这时候为了减少数据库的查询,不增加程序运行的负荷,所以我们在判断用户手机号是否注册过的时候,不再进行查询,而是在保存用户注册数据时候,判断手机号码是否存在,因之前在models中创建数据库表时,将mobile字段的数据设置为唯一约束,所以当数据库ih_user_profile表中已存在mobile字段的数据时,再进行插入相同手机号mobile的数据时,mysql数据库则会抛出异常,那么我们就利用数据库抛出的异常来判断手机号码是否存在,从而减少在视图函数中数据库的查询

数据库测试mobile字段异常

  •  step10 注释step8代码重新编写,大体如下,因密码需要进行加密后再存到数据库,所以到最后面再进行处理

说明:这里的注册名为手机号,后面会另写接口让name为名字昵称,当出现异常时候,需要进行数据库回滚操作

  • step11 将登陆状态保存到session中,并返回注册成功结果
session["name"] = mobile
session["mobile"] = mobile
session["user_id"] = user.id

return jsonify(errno=RET.OK, errmsg="注册成功")
  • step12 将用户输入的密码进行加密

说明:将对用户输入的密码添加salt值(盐值),再进行加密存储,说白了就是,在户输入的密码基础上再添加一个随机生成的字符串,即使用户1和用户2密码一样,但在进行salt处理后,在数据库中也不会相同,不存在撞库说法,这样做安全性极高,即使密码泄露,破解出来后也并不是用户的真是密码

问题:反过来当用户注册成功后,进行用户登录时,那么这个密码是如何进行判断?是将用户输入的密码进行加密后再和数据库加密密码进行对比,可是当初在注册时对用户输入的密码进行加密时候添加了随机的salt值,所以这里需要注意思考一下,我们这里的想法是通过在注册时候将salt值进行保存在数据库中另起一个字段,这是其一方法,其二是将这个salt值与加密后的密码中间以某个分隔符进行分隔,这样做的话就不用再创建表字段另存salt值,如qwe@ohcuncacaucaooa08c03qoca08q08,@符前为salt值,后为用户加密后的密码

:MD5已被攻破,利用暴力测试以及算法公式成功反推出加密前的密码,sh1估计等不了几年就会被破解,我们这里使用的是sha256进行密码的加密

①在models文件中通过导入werkzeug.security类中的generate_password_hash方法将密码进行加密,并存到数据库中

def generate_password_hash(self, origin_password):
    self.password_hash = generate_password_hash(origin_password)

②在passport文件中,在构建User对象的时候,通过调用models文件中的generate_password_hash方法,将前端用户输入的password传参给此方法,并保存到User模型类的passwor_hash字段中

user.generate_password_hash(password)

4.property装饰器的使用

  • step1 将函数调用转换为对象属性设置(在passport文件中)
user.password = password
  • step2 在models中注释之前的generate_password_hash方法的调用,使用property装饰器对新创建的password函数装饰为password属性(注,在passport文件中user.password对象的password属性名要与models文件中的方法同名),再通过password属性的setter方法,设置类属性的值,这个值就是我们在passport文件中通过user.password = password这个password值

四丶注册后端测试

  • step1 因测试users注册后端接口使用的POST请求方式,而此时还没有编写注册前端端口测试,所以需要在ihome/init文件中将csrf后端机制关闭,再进行测试
# 为flask补充csrf防护
# CSRFProtect(app)

  • step3 在注册页面输入错误手机号,正确的图片验证码,当点击获取短信验证码时候,会提示发送短信失败,无所谓,我们从redis数据库直接读取sms_code即可

  • step4 在redis数据库中获取短信验证码(因为在上一步输入的是错误手机号,导致无法发送短信,测试无所谓嘛)

  • step5 在测试工具Postman中,添加127.0.0.1:5000/api/v1.0/users地址,构造json格式的body体数据,就是我们注册页面所需要的数据,成功返回注册成功响应数据,代表注册后端接口没问题

  • step6 响应数据表示注册成功后,查看数据库ih_user_profile表信息,第一条数据是之前测试使用的

五丶注册前端编写

1.前端注册HTML代码(不展示)

2.前端注册JS代码(不展示)

3.在register.js文件中通过ajax方式向后端接口发送注册请求(在callback for submit())

// 调用ajax向后端发送注册请求
        var req_data = {
            mobile:mobile,
            sms_code:phoneCode,
            password:passwd,
            password2:passwd2

        };
        var req_json = JSON.stringify(req_data)
        $.ajax({
            url:"/api/v1.0/users",
            type:"post",
            data:req_json,
            contentType:"application/json",
            dataType: "json",
            headers:{
              "X-CSRFToken":getCookie("csrf_token")
            },
            success: function (resp) {
                if (resp.errno== "0"){
                    // 注册成功,即跳转到主页
                    location.href = "/index.html";
                }else {
                    alert(resp.errmsg);
                }
                
            }

        })

4.在js构建一个getCookie的方法来获取前端名为csrf_token的cookie的值

function getCookie(name) {
    var r = document.cookie.match("\\b" + name + "=([^;]*)\\b");
    return r ? r[1] : undefined;
}

 5.将获取的cookie的值,放在请求头中的X-CSRFToken键中,方便后端进行csrf验证(需将ihome/init中的CSRF验证开启)

headers:{
          "X-CSRFToken":getCookie("csrf_token")
        },

六丶用户注册功能测试

1.进行注册测试,输入不存在的号码是为了演示给大家,虽然提示发送短信验证码失败,但我可以从redis数据库去拿取短信验证码,注册成功后,即跳转到登录页面

2.用户注册成功后,查看数据库中ih_user_profile表的内容,成功将用户注册信息保存到数据库

猜你喜欢

转载自blog.csdn.net/qq_41782425/article/details/85934776