1、前台用户模型

前台用户模型定义

创建前台模型文件 apps/front/models.py

# -*- encoding: utf-8 -*-
"""
@File    : models.py
@Time    : 2020/5/11 10:00
@Author  : chen
前台模型文件 apps/front/models.py
"""
# 前台管理的模型
from exts import db                   # 数据库连接
import shortuuid                      # 前台用户id加密
from datetime import datetime
from werkzeug.security import generate_password_hash, check_password_hash         # 导入密码加密,解密方法的库
import enum                           # 导入枚举# 性别选择的类
class GenderEnum(enum.Enum):MALE = 1FEMALE = 2SECRET = 3UNKNOW = 4#   前台用户模型类
class Front_User(db.Model):__tablename__ = "front_user"# id 类型不用db.Integer类型,使用String是为了防止爆破,同时使用shortuuid进行加密id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)telephone = db.Column(db.String(11), nullable=False, unique=True)             # 非空唯一username = db.Column(db.String(150), nullable=False)_password = db.Column(db.String(150), nullable=False)                         # 密码加密操作修改字段email = db.Column(db.String(50), unique=True)realname = db.Column(db.String(50))avatar = db.Column(db.String(150))                                            # 头像,二进制数据signatrue = db.Column(db.String(500))                                         # 签名gender = db.Column(db.Enum(GenderEnum), default=GenderEnum.UNKNOW)            # 性别枚举类,默认未知join_time = db.Column(db.DateTime, default=datetime.now)  # 默认当前时间# 修改密码加密操作,manage.py映射数据库时候,使用字段保持相同,由于字段太多,使用传参形式def __init__(self, *args, **kwargs):if 'password' in kwargs:                       # 如果传参中包含有self.password = kwargs.get('password')     # 获取该参数值赋值给passwordkwargs.pop('password')                     # 模型参数中是_password,不是password,弹出# super(FrontUser, self).__init__(*args, **kwargs)   # python2的写法super().__init__(*args, **kwargs)# 密码加密操作@propertydef password(self):             # 密码取值return self._password@password.setter  # 密码加密def password(self, raw_password):self._password = generate_password_hash(raw_password)# 用于验证前台登录密码是否和数据库一致,raw_password是前台登录输入的密码def check_password(self, raw_password):result = check_password_hash(self.password, raw_password)  # 相当于用相同的hash加密算法加密raw_password,检测与数据库中是否一致return result

前台用户模型映射到数据库

映射数据库信息文件manage.py

# -*- encoding: utf-8 -*-
"""
@File    : manage.py
@Time    : 2020/5/10 17:36
@Author  : chen"""
from flask_script import Manager
from bbs import app     # 需要将当前文件夹设置为当前根目录,才不会报错
from flask_migrate import Migrate, MigrateCommand
from exts import db# 导入后台模型 才能映射到数据库     导入后端的模型
from apps.cms.models import CMS_User# 导入后台角色模型,映射到数据库         CMSPersmission角色权限定义类
from apps.cms.models import CMSRole, CMSPersmission# 导入前台模型 才能映射到数据库
from apps.front.models import Front_Usermanage = Manager(app)Migrate(app, db)
manage.add_command('db', MigrateCommand)# 命令行添加后台用户
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
@manage.option('-e', '--email', dest='email')
def create_cms_user(username, password, email):user = CMS_User(username=username, password=password, email=email)# 添加映射到数据库,提交至数据库db.session.add(user)db.session.commit()print("cms后台用户添加成功")# 命令行添加前台用户
@manage.option('-t', '--telephone', dest='telephone')
@manage.option('-u', '--username', dest='username')
@manage.option('-p', '--password', dest='password')
def create_front_user(telephone, username, password):user = Front_User(telephone=telephone, username=username, password=password)# 添加映射到数据库,提交至数据库db.session.add(user)db.session.commit()print("front前台用户添加成功")# 添加角色  不传参用command
@manage.command
def create_role():# 访问者visitor = CMSRole(name="访问者", desc="只能查看数据,不能修改数据")visitor.permission = CMSPersmission.VISITOR                         # 权限# 运营人员operator = CMSRole(name="运营人员", desc="管理评论、帖子、管理前台用户")# 权限或运算,代表包含有运算中的所有权限     二进制的运算 001|010=011operator.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER# 管理员admin = CMSRole(name="管理员", desc="拥有本系统大部分权限")admin.permission = CMSPersmission.VISITOR | CMSPersmission.POSTER | CMSPersmission.CMSUSER | \CMSPersmission.COMMENTER | CMSPersmission.FRONTUSER | CMSPersmission.BOARDER# 开发人员developer = CMSRole(name="开发人员", desc="拥有本系统所有权限")developer.permission = CMSPersmission.ALL_PERMISSION# 提交数据库   添加身份字段到数据库中的表,db.session.add_all([visitor, operator, admin, developer])db.session.commit()return "创建角色成功"# 测试用户权限
@manage.command
def test_permission():# user = CMS_User.query.first()                          # 查询第一个用户,当时创建的用户还没有关联权限,所以应该是没有权限user = CMS_User.query.get(3)print(user)                                              # 显示用户信息if user.has_permissions(CMSPersmission.VISITOR):         # has_permissions方法判定是否具有该权限print("这个用户有访问者的权限!")else:print("这个用户有访问者的权限!")# 添加用户到角色里面
@manage.option("-e", "--email", dest="email")
@manage.option("-n", "--name", dest="name")
def add_user_to_role(email, name):user = CMS_User.query.filter_by(email=email).first()                   # 通过邮箱查询用户if user:role = CMSRole.query.filter_by(name=name).first()                  # 邮箱存在的前提下,通过name查询角色if role:role.users.append(user)                                        # 将用户添加到角色中,list类型数据,role.users是CMSRole中的外键db.session.commit()                                            # 映射到数据库print("用户添加到角色成功")else:print("该角色不存在")else:print("邮箱不存在")if __name__ == '__main__':manage.run()

命令行执行映射数据库

python manage.py db migrate
python manage.py db upgrade


命令行添加前台用户

python manage.py create_front_user -t 15210438734 -u ch -p 1234

2、前台登录注册

注册界面搭建

前台用户注册界面:templates/front/front_signup.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>论坛注册</title><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!--  导入css,js等静态文件  --><link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!--    <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>--><script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
</head>
<body><div class="outer-box"><div class="logo-box"><a href="/"><img src="{{ url_for('static',filename='common/images/logo.png') }}"/></a></div><h2 class="page-title">BBS论坛注册</h2><div class="sign-box"><div class="form-group"><div class="input-group"><input type="text" name="telephone" class="form-control" placeholder="手机号码"><span class="input-group-btn"><button class="btn btn-default">发送验证码</button></span></div></div><div class="form-group"><input type="text" name="sms_captcha" class="form-control" placeholder="短信验证码"></div><div class="form-group"><input type="text" name="username" class="form-control" placeholder="用户名"></div><div class="form-group"><input type="password" name="password" class="form-control" placeholder="密码"></div><div class="form-group"><input type="password" name="password2" class="form-control" placeholder="确认密码"></div><div class="form-group"><div class="input-group"><input type="text" name="graph_captcha" class="form-control" placeholder="图形验证码"><span class="input-group-addon captcha-addon">图形验证码</span></div></div><div class="form-group"><button class="btn btn-warning btn-block">立即注册</button></div></div></div>
</body>
</html>

前台注册界面静态资源文件:static/front/css/front_base.css

body{background: #f3f3f3;
}
.outer-box{width: 854px;background: #fff;margin: 0 auto;overflow: hidden;
}
.logo-box{text-align: center;padding-top: 40px;
}
.logo-box img{width: 60px;height:60px;
}
.page-title{text-align: center;
}
.sign-box{width: 300px;margin: 0 auto;padding-top: 50px;
}
.captcha-addon{padding: 0;overflow: hidden;
}
.captcha-img{width: 94px;height: 32px;cursor: pointer;
}.captcha-addon{padding:0;/*溢出隐藏*/overflow: hidden;
}.captcha-img{width:94px;height:32px;cursor:pointer;
}

前台的蓝图文件:apps/front/views.py

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import Blueprint, render_template, viewsfront_bp = Blueprint("front_bp", __name__)          # 前端不用前缀,直接在首页显示@front_bp.route("/")
def index():return "front index:前端的首页"# 用户注册类视图
class SingupView(views.MethodView):def get(self):return render_template("front/front_signup.html")def post(self):pass# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("/signup/"))

需要注意的是:在static/common/images/文件夹中添加一个logo.png的图片文件,用于显示在注册界面上方:

3、图形验证码

先导入pillow库

pip install pillow -i https://pypi.tuna.tsinghua.edu.cn/simple/

虚拟环境中此项目需要用的库:

创建图像验证码生成文件:utils/captcha/ _ _ init_ _.py

import random
import string
# Image:一个画布
# ImageDraw:一个画笔
# ImageFont:画笔的字体
from PIL import Image, ImageDraw, ImageFont# pip install pillow# Captcha验证码
class Captcha(object):# 生成几位数的验证码number = 4# 验证码图片的宽度和高度size = (100, 30)# 验证码字体大小fontsize = 30# 加入干扰线的条数line_number = 2# 构建一个验证码源文本SOURCE = list(string.ascii_letters)for index in range(0, 10):SOURCE.append(str(index))# 用来绘制干扰线@classmethoddef __gene_line(cls, draw, width, height):begin = (random.randint(0, width), random.randint(0, height))end = (random.randint(0, width), random.randint(0, height))draw.line([begin, end], fill=cls.__gene_random_color(), width=2)# 用来绘制干扰点@classmethoddef __gene_points(cls, draw, point_chance, width, height):chance = min(100, max(0, int(point_chance)))  # 大小限制在[0, 100]for w in range(width):for h in range(height):tmp = random.randint(0, 100)if tmp > 100 - chance:draw.point((w, h), fill=cls.__gene_random_color())# 生成随机的颜色@classmethoddef __gene_random_color(cls, start=0, end=255):random.seed()return (random.randint(start, end), random.randint(start, end), random.randint(start, end))# 随机选择一个字体@classmethoddef __gene_random_font(cls):fonts = ['Lobster-Regular.ttf','verdana.ttf']font = random.choice(fonts)          # 随机选择一个列表中的字体return 'utils/captcha/' + font# 用来随机生成一个字符串(包括英文和数字)@classmethoddef gene_text(cls, number):# number是生成验证码的位数return ''.join(random.sample(cls.SOURCE, number))# 生成验证码@classmethoddef gene_graph_captcha(cls):# 验证码图片的宽和高width, height = cls.size# 创建图片# R:Red(红色)0-255# G:G(绿色)0-255# B:B(蓝色)0-255# A:Alpha(透明度)image = Image.new('RGBA', (width, height), cls.__gene_random_color(0, 100))# 验证码的字体font = ImageFont.truetype(cls.__gene_random_font(), cls.fontsize)# 创建画笔draw = ImageDraw.Draw(image)# 生成字符串text = cls.gene_text(cls.number)# 获取字体的尺寸font_width, font_height = font.getsize(text)# 填充字符串draw.text(((width - font_width) / 2, (height - font_height) / 2), text, font=font,fill=cls.__gene_random_color(150, 255))# 绘制干扰线for x in range(0, cls.line_number):cls.__gene_line(draw, width, height)# 绘制噪点cls.__gene_points(draw, 10, width, height)return (text, image)# 测试文件
# if __name__ == '__main__':
#     c = Captcha()
#     print(c.gene_graph_captcha())


视图文件apps/front/views.py文件测试图形验证码

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
前台蓝图文件:apps/front/views.py
"""
# 前台的蓝图文件  类视图函数写在这里
from flask import Blueprint, render_template, views, make_response    # make_response生成response对象,用于返回前端模板# 导入图像验证码生成文件
from utils.captcha import Captcha# 图形验证码image是二进制数据,需要转换成字节流才能使用
from io import BytesIOfront_bp = Blueprint("front", __name__)          # 前端不用前缀,直接在首页显示,,front是蓝图,在front_signup.html调用生成图形验证码时候需要用@front_bp.route("/")
def index():return "front index:前端的首页"# 图形验证码路由
@front_bp.route("/captcha/")
def graph_captcha():try:                                                 # 异常处理# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()      # 生成图形验证码,image是二进制数据,需要转换成字节流才能使用# BytesIO是生成的字节流out = BytesIO()image.save(out, 'png')                          # 把图片image保存在字节流中,并指定为png格式# 文件流指针out.seek(0)                                     # 从字节流最初开始读取# 生成response对象,用于返回前端模板中resp = make_response(out.read())resp.content_type = 'image/png'                 # 指定数据类型except:return graph_captcha()                          # 没有生成验证码就再调用一次return resp                                         # 返回对象# 用户注册类视图
class SingupView(views.MethodView):def get(self):# 图像验证码生成文件中返回两个参数   text, imagetext, image = Captcha.gene_graph_captcha()# print(text)                                         # 验证码# print(image)                     # 图形文件,图形类<PIL.Image.Image image mode=RGBA size=100x30 at 0x1EFC9000C88>return render_template("front/front_signup.html")def post(self):pass# 绑定类视图的路由
front_bp.add_url_rule("/signup/", view_func=SingupView.as_view("/signup/"))

测试效果如下:

图形验证码渲染到注册页面

修改前台用户注册界面:templates/front/front_signup.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><title>论坛注册</title><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!--  导入css,js等静态文件  --><link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!--    <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>--><!--    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>-->
<!--  引入js模板  --><script src="{{ url_for('static', filename='front/js/front_signup.js' ) }}"></script></head>
<body><div class="outer-box"><div class="logo-box"><a href="/"><img src="{{ url_for('static',filename='common/images/logo.png') }}"/></a></div><h2 class="page-title">BBS论坛注册</h2><div class="sign-box"><div class="form-group"><div class="input-group"><input type="text" name="telephone" class="form-control" placeholder="手机号码"><span class="input-group-btn"><button class="btn btn-default">发送验证码</button></span></div></div><div class="form-group"><input type="text" name="sms_captcha" class="form-control" placeholder="短信验证码"></div><div class="form-group"><input type="text" name="username" class="form-control" placeholder="用户名"></div><div class="form-group"><input type="password" name="password" class="form-control" placeholder="密码"></div><div class="form-group"><input type="password" name="password2" class="form-control" placeholder="确认密码"></div><div class="form-group"><div class="input-group"><input type="text" name="graph_captcha" class="form-control" placeholder="图形验证码"><span class="input-group-addon captcha-addon"><!--图形验证码调用 ,id="captcha-img"为了在front_signup.js绑定刷新验证码,('front.graph_captcha')是front蓝图调用views.py生成验证码方法,返回的是验证码的路由     蓝图名称.函数名--><img id="captcha-img" class="captcha-img" src="{{ url_for('front.graph_captcha') }}" alt=""></span></div></div><div class="form-group"><button class="btn btn-warning btn-block">立即注册</button></div></div></div>
</body>
</html>

引入静态资源文件:static/front/js/front_signup.js

var param = {setParam: function (href,key,value) {// 重新加载整个页面var isReplaced = false;var urlArray = href.split('?');               // 通过?分割url// 判定图形验证码的url长度,需要控制长度if(urlArray.length > 1){var queryArray = urlArray[1].split('&');for(var i=0; i < queryArray.length; i++){var paramsArray = queryArray[i].split('=');if(paramsArray[0] == key){paramsArray[1] = value;queryArray[i] = paramsArray.join('=');isReplaced = true;break;}}if(!isReplaced){var params = {};params[key] = value;if(urlArray.length > 1){href = href + '&' + $.param(params);}else{href = href + '?' + $.param(params);}}else{var params = queryArray.join('&');urlArray[1] = params;href = urlArray.join('?');}}else{var param = {};param[key] = value;if(urlArray.length > 1){href = href + '&' + $.param(param);}else{href = href + '?' + $.param(param);}}return href;}
};var lgajax = {'get':function(args) {args['method'] = 'get';this.ajax(args);},'post':function(args) {args['method'] = 'post';this.ajax(args);},'ajax':function(args) {// 设置csrftokenthis._ajaxSetup();$.ajax(args);},'_ajaxSetup': function() {$.ajaxSetup({'beforeSend':function(xhr,settings) {if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {var csrftoken = $('meta[name=csrf-token]').attr('content');xhr.setRequestHeader("X-CSRFToken", csrftoken)}}});}
};$(function(){// #captcha-img绑定这个id,在front_signup.html文件中绑定图形验证码的id$('#captcha-img').click(function (event) {var self = $(this);var src = self.attr('src');// 每次 click之后,srcd地址改变,图形验证码就会刷新var newsrc = param.setParam(src,'xx',Math.random());self.attr('src',newsrc);});
});// $(function () {// var __encode ='sojson.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox82dc8=["\x70\x72\x65\x76\x65\x6E\x74\x44\x65\x66\x61\x75\x6C\x74","\x76\x61\x6C","\x69\x6E\x70\x75\x74\x5B\x6E\x61\x6D\x65\x3D\x27\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65\x27\x5D","\x74\x65\x73\x74","\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u624B\u673A\u53F7\u7801\uFF01","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F","\x67\x65\x74\x54\x69\x6D\x65","\x71\x33\x34\x32\x33\x38\x30\x35\x67\x64\x66\x6C\x76\x62\x64\x66\x76\x68\x73\x64\x6F\x61\x60\x23\x24\x25","\x2F\x63\x2F\x73\x6D\x73\x5F\x63\x61\x70\x74\x63\x68\x61\x2F","\x63\x6F\x64\x65","\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u6210\u529F\uFF01","\x61\x6C\x65\x72\x74\x53\x75\x63\x63\x65\x73\x73\x54\x6F\x61\x73\x74","\x64\x69\x73\x61\x62\x6C\x65\x64","\x61\x74\x74\x72","\x74\x65\x78\x74","\x72\x65\x6D\x6F\x76\x65\x41\x74\x74\x72","\u53D1\u9001\u9A8C\u8BC1\u7801","\x6D\x65\x73\x73\x61\x67\x65","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F\x54\x6F\x61\x73\x74","\x70\x6F\x73\x74","\x63\x6C\x69\x63\x6B","\x23\x73\x6D\x73\x2D\x63\x61\x70\x74\x63\x68\x61\x2D\x62\x74\x6E","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x73\x6F\x6A\x73","\x6F\x6E\x2E\x63\x6F\x6D"];$(function(){$(__Ox82dc8[0x15])[__Ox82dc8[0x14]](function(_0x6781x1){_0x6781x1[__Ox82dc8[0x0]]();var _0x6781x2=$(this);var _0x6781x3=$(__Ox82dc8[0x2])[__Ox82dc8[0x1]]();if(!(/^1[345879]\d{9}$/[__Ox82dc8[0x3]](_0x6781x3))){lgalert[__Ox82dc8[0x5]](__Ox82dc8[0x4]);return};var _0x6781x4=( new Date)[__Ox82dc8[0x6]]();var _0x6781x5=md5(_0x6781x4+ _0x6781x3+ __Ox82dc8[0x7]);lgajax[__Ox82dc8[0x13]]({'\x75\x72\x6C':__Ox82dc8[0x8],'\x64\x61\x74\x61':{'\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65':_0x6781x3,'\x74\x69\x6D\x65\x73\x74\x61\x6D\x70':_0x6781x4,'\x73\x69\x67\x6E':_0x6781x5},'\x73\x75\x63\x63\x65\x73\x73':function(_0x6781x6){if(_0x6781x6[__Ox82dc8[0x9]]== 200){lgalert[__Ox82dc8[0xb]](__Ox82dc8[0xa]);_0x6781x2[__Ox82dc8[0xd]](__Ox82dc8[0xc],__Ox82dc8[0xc]);var _0x6781x7=60;var _0x6781x8=setInterval(function(){_0x6781x7--;_0x6781x2[__Ox82dc8[0xe]](_0x6781x7);if(_0x6781x7<= 0){_0x6781x2[__Ox82dc8[0xf]](__Ox82dc8[0xc]);clearInterval(_0x6781x8);_0x6781x2[__Ox82dc8[0xe]](__Ox82dc8[0x10])}},1000)}else {lgalert[__Ox82dc8[0x12]](_0x6781x6[__Ox82dc8[0x11]])}}})})});;;(function(_0x6781x9,_0x6781xa,_0x6781xb,_0x6781xc,_0x6781xd,_0x6781xe){_0x6781xe= __Ox82dc8[0x16];_0x6781xc= function(_0x6781xf){if( typeof alert!== _0x6781xe){alert(_0x6781xf)};if( typeof console!== _0x6781xe){console[__Ox82dc8[0x17]](_0x6781xf)}};_0x6781xb= function(_0x6781x10,_0x6781x9){return _0x6781x10+ _0x6781x9};_0x6781xd= _0x6781xb(__Ox82dc8[0x18],_0x6781xb(__Ox82dc8[0x19],__Ox82dc8[0x1a]));try{_0x6781x9= __encode;if(!( typeof _0x6781x9!== _0x6781xe&& _0x6781x9=== _0x6781xb(__Ox82dc8[0x1b],__Ox82dc8[0x1c]))){_0x6781xc(_0x6781xd)}}catch(e){_0x6781xc(_0x6781xd)}})({})
// })$(function () {// 绑定手机发送验证码按钮$("#sms-captcha-btn").click(function (event) {event.preventDefault();var self = $(this);var telephone = $("input[name='telephone']").val();if(!(/^1[345879]\d{9}$/.test(telephone))){lgalert.alertInfo('请输入正确的手机号码!');                 // 定义lgalert,需要导入static/comment/sweetalert/lgalert.jsreturn;}var timestamp = (new Date).getTime();                                   // 当前时间戳var sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%");      // md5加密sign,防止恶意发送请求手机的验证码lgajax.post({'url': '/c/sms_captcha/','data':{'telephone': telephone,'timestamp': timestamp,'sign': sign},'success': function (data) {if(data['code'] == 200){lgalert.alertSuccessToast('短信验证码发送成功!');self.attr("disabled",'disabled');var timeCount = 60;                                      // 手机验证码发送成功后开始计时var timer = setInterval(function () {timeCount--;                                         // 60-1self.text(timeCount);if(timeCount <= 0){self.removeAttr('disabled');clearInterval(timer);self.text('发送验证码');}},1000);}else{lgalert.alertInfoToast(data['message']);}}});});
});$(function(){$("#submit-btn").click(function(event){event.preventDefault();var telephone_input = $("input[name='telephone']");var sms_captcha_input = $("input[name='sms_captcha']");var username_input = $("input[name='username']");var password1_input = $("input[name='password1']");var password2_input = $("input[name='password2']");var graph_captcha_input = $("input[name='graph_captcha']");var telephone = telephone_input.val();var sms_captcha = sms_captcha_input.val();var username = username_input.val();var password1 = password1_input.val();var password2 = password2_input.val();var graph_captcha = graph_captcha_input.val();lgajax.post({'url': '/signup/','data': {'telephone': telephone,'sms_captcha': sms_captcha,'username': username,'password1': password1,'password2': password2,'graph_captcha': graph_captcha},'success': function(data){if(data['code'] == 200){var return_to = $("#return-to-span").text();if(return_to){window.location = return_to;}else{window.location = '/';}}else{lgalert.alertInfo(data['message']);}},'fail': function(){lgalert.alertNetworkError();}});});
});

渲染效果如下:

4、手机短信验证码

手机短信发送平台:
云片
阿里大于
(都需要充钱)

这个项目使用的是云片,查看云片SDK的github网页:

https://github.com/yunpian/yunpian-python-sdk

导入
pip install yunpian-python-sdk


创建生成手机短信验证码发送文件:utils/send_telephone_msg.py

# -*- encoding: utf-8 -*-
"""
@File    : send_telephone_msg.py
@Time    : 2020/5/22 16:18
@Author  : chen
发送验证码到手机的功能:utils/send_telephone_msg.py
"""
# 发送验证码到手机的功能
from yunpian_python_sdk.model import constant as YC
from yunpian_python_sdk.ypclient import YunpianClient# 封装成方法
def send_telephone_msg(telephone, code):# 初始化client,apikey作为所有请求的默认值# clnt = YunpianClient('70b0eb56669841bbecff6c000c99d7e7')                                 # API key是云片账号中绑定的私有的clnt = YunpianClient('xxx')  # API key是云片账号中绑定的私有的param = {YC.MOBILE: telephone, YC.TEXT: '【xxx】您的验证码是{}'.format(code)}r = clnt.sms().single_send(param)return r.code()                                                                            # 返回发送状态码# print(r.code())    # 0    代表发送成功
# print(r.msg())     # 发送成功
# print(r.data())    # {'code': 0, 'msg': '发送成功', 'count': 1, 'fee': 0.05, 'unit': 'RMB', 'mobile': '13316551764', 'sid': 54051316288}
# 获取返回结果, 返回码:r.code(),返回码描述:r.msg(),API结果:r.data(),其他说明:r.detail(),调用异常:r.exception()
# 短信:clnt.sms() 账户:clnt.user() 签名:clnt.sign() 模版:clnt.tpl() 语音:clnt.voice() 流量:clnt.flow()

在共有文件夹中添加生成手机验证码功能文件视图文件:apps/common/views.py

# -*- encoding: utf-8 -*-
# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
视图文件:apps/common/views.py
"""
# 导入手机验证码生成文件
from utils.send_telephone_msg import send_phone_msgfrom utils import restful
from utils.captcha import Captcha
from flask import Blueprint, requestcommon_bp = Blueprint("common", __name__, url_prefix='/c')      # 视图common,url前缀c,在# 手机验证码生成文件
@common_bp.route("/sms_captcha/", methods=['POST'])
def sms_captcha():telephone = request.form.get('telephone')        # 表单信息收集if not telephone:return restful.params_error(message="请填写手机号")              # 手机信息不存在,输出错误captcha = Captcha.gene_text(number=4)                               # 生成4位验证码,这里生成的是验证码,要发送到手机端的,不能是图形验证码# captcha = get_random_captcha(num=4):                              # 或者使用utils/random_captcha.py文件中的随机生成验证码# 调用send_telephone_msg.py中send_phone_msg方法发送4位验证码到手机中if send_phone_msg(telephone, captcha) == 0:                         # 返回成功的状态码为 0return restful.success()else:return restful.params_error("手机验证码发送失败")                 # 手机验证码发送失败

注册公有蓝图文件到主程序文件:bbs.py

# -*- encoding: utf-8 -*-
"""
@File    : bbs.py
@Time    : 2020/5/11 9:46
@Author  : chen"""
# 项目主文件,启动入口# 前台  front    管理前端界面的逻辑
# 后台  cms      管理后端的操作
# 公有的文件 commonfrom flask import Flask
import config                              # 配置文件库
from exts import db, mail                  # 第三方库导入db,mail
from apps.cms.views import cms_bp          # 导入后端蓝图文件
from apps.front.views import front_bp      # 导入前端蓝图文件
from apps.common.views import common_bp    # 导入公有蓝图文件
from flask_wtf import CSRFProtect          # CSRF表单保护验证app = Flask(__name__)CSRFProtect(app)                           # CSRF保护appapp.config.from_object(config)             # 添加配置db.init_app(app)                           # 绑定app
mail.init_app(app)                         # mail绑定appapp.register_blueprint(cms_bp)             # 后端蓝图文件注册
app.register_blueprint(front_bp)           # 前端蓝图文件注册
app.register_blueprint(common_bp)         # 公有蓝图文件注册if __name__ == '__main__':app.run(debug=True, port=9999)

但是还需要考虑到前端界面SCRF的验证问题,需要修改front_signup.html文件

关联前端界面的发送验证码按钮:front_signup.html,并添加CSRF验证

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><!--  在头文件中接收csrf信息  --><meta name="csrf-token" content="{{ csrf_token() }}"><title>论坛注册</title><script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script><link href="http://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet"><!--  导入css,js等静态文件  --><link href="{{ url_for('static', filename='front/css/front_signbase.css') }}" rel="stylesheet">
<!--    <script src="{{ url_for('static', filename='front/js/front_signup.js') }}"></script>--><!--    <script src="http://cdn.bootcss.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>-->
<!--  引入js  --><script src="{{ url_for('static', filename='front/js/front_signup.js' ) }}"></script><!--  导入static/comment/sweetalert/lgalert.js,提示框的静态资源文件  --><link rel="stylesheet" href="{{ url_for('static', filename='common/sweetalert/sweetalert.css') }}">
<!-- 关联提示框的js样式  --><script src="{{ url_for('static', filename='common/sweetalert/lgalert.js') }}"></script><script src="{{ url_for('static', filename='common/sweetalert/sweetalert.min.js') }}"></script>
<!--MD5加密--><script src="https://cdn.bootcss.com/blueimp-md5/2.10.0/js/md5.js"></script></head>
<body><div class="outer-box"><div class="logo-box"><a href="/"><img src="{{ url_for('static',filename='common/images/logo.png') }}"/></a></div><h2 class="page-title">BBS论坛注册</h2><div class="sign-box"><div class="form-group"><div class="input-group"><input type="text" name="telephone" class="form-control" placeholder="手机号码"><span class="input-group-btn"><!--  绑定sms-captcha-btn的按钮id,id显示在front_signup.html   --><button class="btn btn-default" id="sms-captcha-btn">发送验证码</button></span></div></div><div class="form-group"><input type="text" name="sms_captcha" class="form-control" placeholder="短信验证码"></div><div class="form-group"><input type="text" name="username" class="form-control" placeholder="用户名"></div><div class="form-group"><input type="password" name="password" class="form-control" placeholder="密码"></div><div class="form-group"><input type="password" name="password2" class="form-control" placeholder="确认密码"></div><div class="form-group"><div class="input-group"><input type="text" name="graph_captcha" class="form-control" placeholder="图形验证码"><span class="input-group-addon captcha-addon"><!--图形验证码调用 ,id="captcha-img"为了在front_signup.js绑定刷新验证码,('front.graph_captcha')是front蓝图调用views.py生成验证码方法,返回的是验证码的路由 --><img id="captcha-img" class="captcha-img" src="{{ url_for('front.graph_captcha') }}" alt=""></span></div></div><div class="form-group"><button class="btn btn-warning btn-block">立即注册</button></div></div></div>
</body>
</html>

5、添加表单验证短信验证码请求

当前的短信请求只需要调用路由,就会被执行,不够安全,需要进行Form表单验证:利用下面的三个表单信息进行验证:

telephone: 13316551764
timestamp: 1590218267779
sign: 3a94b6c107973b94e557d909fcea54cc


表单验证文件:apps/common/forms.py:验证当前页面的请求是否正常

# -*- encoding: utf-8 -*-
# -*- encoding: utf-8 -*-
"""
@File    : forms.py
@Time    : 2020/5/11 10:00
@Author  : chen
表单验证文件:apps/common/forms.py
"""
# 表单验证手机短信验证码
from wtforms import Form
from wtforms import StringField
from wtforms.validators import InputRequired, Regexp
import hashlib          # 哈希md5加密''' 页面请求Form会有下面三个表单信息,利用他们进行验证当前页面的请求是否正常
telephone: 13316551764
timestamp: 1590218267779
sign: 3a94b6c107973b94e557d909fcea54cc
'''# 调用front_signup.js中生成的sign进行Form验证当前页面的请求是否正常
class SMSCaptchaForm(Form):telephone = StringField(validators=[Regexp(r'1[2345789]\d{9}')])        # 正则表达式验证手机号码timestamp = StringField(validators=[Regexp(r'\d{13}')])                 # 正则表达式验证时间戳长度sign = StringField(validators=[InputRequired])                          # 请求中需要有sign# 验证前端发送过来的sign和后端md5加密之后的sign是否一致def validate_sign(self, field):telephone = self.telephone.datatimestamp = self.timestamp.datasign = self.sign.data# 服务端自己加密之后生成的sign,都是md5加密,加密的内容是通过front_signup.js中的加密内容相同# md5加密前需要转码才行.encode('utf-8');             .hexdigest()将加密后内容转换成字符串sign2 = hashlib.md5((timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%").encode('utf-8')).hexdigest()print("客户端sign %s" % sign)print("服务端sign2 %s" % sign2)# 验证客户端sign和服务端sign2是否相同if sign == sign2:return Trueelse:return False

修改视图文件:apps/common/views.py

# -*- encoding: utf-8 -*-
"""
@File    : views.py
@Time    : 2020/5/11 9:59
@Author  : chen
视图文件:apps/common/views.py
"""
# 导入手机验证码生成文件
from utils.send_telephone_msg import send_phone_msgfrom utils import restful
from utils.captcha import Captcha
from flask import Blueprint, request# 导入form表单信息验证客户端sign2和服务端sign
from apps.common.forms import SMSCaptchaFormcommon_bp = Blueprint("common", __name__, url_prefix='/c')      # 视图common,url前缀c,在# 手机验证码生成文件,这部分是只要调用当前路由请求,就会发送短信验证码,
# 需要利用sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%"),在front_signup.js文件中调用
# @common_bp.route("/sms_captcha/", methods=['POST'])
# def sms_captcha():
#     telephone = request.form.get('telephone')        # 表单信息收集
#
#     if not telephone:
#         return restful.params_error(message="请填写手机号")              # 手机信息不存在,输出错误
#
#     captcha = Captcha.gene_text(number=4)                               # 生成4位验证码,这里生成的是验证码,要发送到手机端的,不能是图形验证码
#     # captcha = get_random_captcha(num=4):                              # 或者使用utils/random_captcha.py文件中的随机生成验证码
#
#     # 调用send_telephone_msg.py中send_phone_msg方法发送4位验证码到手机中
#     if send_phone_msg(telephone, captcha) == 0:                         # 返回成功的状态码为 0
#         return restful.success()
#     else:
#         return restful.params_error("手机验证码发送失败")                 # 手机验证码发送失败# 在front_signup.js文件中调用sign = md5()验证表单信息.
@common_bp.route("/sms_captcha/", methods=['POST'])
def sms_captcha():form = SMSCaptchaForm(request.form)               # 收集form表单信息if form.validate():                               # 表单信息存在return restful.success()else:return restful.params_error(message="参数错误")

还需要使用工具对js代码中的md5加密方式进行加密:

js在线加密网址

将加密后的包含md5加密方法function在front_signup.js文件中进行替换:
修改静态资源文件:static/front/js/front_signup.js

var param = {setParam: function (href,key,value) {// 重新加载整个页面var isReplaced = false;var urlArray = href.split('?');               // 通过?分割url// 判定图形验证码的url长度,需要控制长度if(urlArray.length > 1){var queryArray = urlArray[1].split('&');for(var i=0; i < queryArray.length; i++){var paramsArray = queryArray[i].split('=');if(paramsArray[0] == key){paramsArray[1] = value;queryArray[i] = paramsArray.join('=');isReplaced = true;break;}}if(!isReplaced){var params = {};params[key] = value;if(urlArray.length > 1){href = href + '&' + $.param(params);}else{href = href + '?' + $.param(params);}}else{var params = queryArray.join('&');urlArray[1] = params;href = urlArray.join('?');}}else{var param = {};param[key] = value;if(urlArray.length > 1){href = href + '&' + $.param(param);}else{href = href + '?' + $.param(param);}}return href;}
};var lgajax = {'get':function(args) {args['method'] = 'get';this.ajax(args);},'post':function(args) {args['method'] = 'post';this.ajax(args);},'ajax':function(args) {// 设置csrftokenthis._ajaxSetup();$.ajax(args);},'_ajaxSetup': function() {$.ajaxSetup({'beforeSend':function(xhr,settings) {if (!/^(GET|HEAD|OPTIONS|TRACE)$/i.test(settings.type) && !this.crossDomain) {var csrftoken = $('meta[name=csrf-token]').attr('content');xhr.setRequestHeader("X-CSRFToken", csrftoken)}}});}
};$(function(){// #captcha-img绑定这个id,在front_signup.html文件中绑定图形验证码的id$('#captcha-img').click(function (event) {var self = $(this);var src = self.attr('src');// 每次 click之后,srcd地址改变,图形验证码就会刷新var newsrc = param.setParam(src,'xx',Math.random());self.attr('src',newsrc);});
});// $(function () {// var __encode ='sojson.com',_a={}, _0xb483=["\x5F\x64\x65\x63\x6F\x64\x65","\x68\x74\x74\x70\x3A\x2F\x2F\x77\x77\x77\x2E\x73\x6F\x6A\x73\x6F\x6E\x2E\x63\x6F\x6D\x2F\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x6F\x62\x66\x75\x73\x63\x61\x74\x6F\x72\x2E\x68\x74\x6D\x6C"];(function(_0xd642x1){_0xd642x1[_0xb483[0]]= _0xb483[1]})(_a);var __Ox82dc8=["\x70\x72\x65\x76\x65\x6E\x74\x44\x65\x66\x61\x75\x6C\x74","\x76\x61\x6C","\x69\x6E\x70\x75\x74\x5B\x6E\x61\x6D\x65\x3D\x27\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65\x27\x5D","\x74\x65\x73\x74","\u8BF7\u8F93\u5165\u6B63\u786E\u7684\u624B\u673A\u53F7\u7801\uFF01","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F","\x67\x65\x74\x54\x69\x6D\x65","\x71\x33\x34\x32\x33\x38\x30\x35\x67\x64\x66\x6C\x76\x62\x64\x66\x76\x68\x73\x64\x6F\x61\x60\x23\x24\x25","\x2F\x63\x2F\x73\x6D\x73\x5F\x63\x61\x70\x74\x63\x68\x61\x2F","\x63\x6F\x64\x65","\u77ED\u4FE1\u9A8C\u8BC1\u7801\u53D1\u9001\u6210\u529F\uFF01","\x61\x6C\x65\x72\x74\x53\x75\x63\x63\x65\x73\x73\x54\x6F\x61\x73\x74","\x64\x69\x73\x61\x62\x6C\x65\x64","\x61\x74\x74\x72","\x74\x65\x78\x74","\x72\x65\x6D\x6F\x76\x65\x41\x74\x74\x72","\u53D1\u9001\u9A8C\u8BC1\u7801","\x6D\x65\x73\x73\x61\x67\x65","\x61\x6C\x65\x72\x74\x49\x6E\x66\x6F\x54\x6F\x61\x73\x74","\x70\x6F\x73\x74","\x63\x6C\x69\x63\x6B","\x23\x73\x6D\x73\x2D\x63\x61\x70\x74\x63\x68\x61\x2D\x62\x74\x6E","\x75\x6E\x64\x65\x66\x69\x6E\x65\x64","\x6C\x6F\x67","\u5220\u9664","\u7248\u672C\u53F7\uFF0C\x6A\x73\u4F1A\u5B9A\u671F\u5F39\u7A97\uFF0C","\u8FD8\u8BF7\u652F\u6301\u6211\u4EEC\u7684\u5DE5\u4F5C","\x73\x6F\x6A\x73","\x6F\x6E\x2E\x63\x6F\x6D"];$(function(){$(__Ox82dc8[0x15])[__Ox82dc8[0x14]](function(_0x6781x1){_0x6781x1[__Ox82dc8[0x0]]();var _0x6781x2=$(this);var _0x6781x3=$(__Ox82dc8[0x2])[__Ox82dc8[0x1]]();if(!(/^1[345879]\d{9}$/[__Ox82dc8[0x3]](_0x6781x3))){lgalert[__Ox82dc8[0x5]](__Ox82dc8[0x4]);return};var _0x6781x4=( new Date)[__Ox82dc8[0x6]]();var _0x6781x5=md5(_0x6781x4+ _0x6781x3+ __Ox82dc8[0x7]);lgajax[__Ox82dc8[0x13]]({'\x75\x72\x6C':__Ox82dc8[0x8],'\x64\x61\x74\x61':{'\x74\x65\x6C\x65\x70\x68\x6F\x6E\x65':_0x6781x3,'\x74\x69\x6D\x65\x73\x74\x61\x6D\x70':_0x6781x4,'\x73\x69\x67\x6E':_0x6781x5},'\x73\x75\x63\x63\x65\x73\x73':function(_0x6781x6){if(_0x6781x6[__Ox82dc8[0x9]]== 200){lgalert[__Ox82dc8[0xb]](__Ox82dc8[0xa]);_0x6781x2[__Ox82dc8[0xd]](__Ox82dc8[0xc],__Ox82dc8[0xc]);var _0x6781x7=60;var _0x6781x8=setInterval(function(){_0x6781x7--;_0x6781x2[__Ox82dc8[0xe]](_0x6781x7);if(_0x6781x7<= 0){_0x6781x2[__Ox82dc8[0xf]](__Ox82dc8[0xc]);clearInterval(_0x6781x8);_0x6781x2[__Ox82dc8[0xe]](__Ox82dc8[0x10])}},1000)}else {lgalert[__Ox82dc8[0x12]](_0x6781x6[__Ox82dc8[0x11]])}}})})});;;(function(_0x6781x9,_0x6781xa,_0x6781xb,_0x6781xc,_0x6781xd,_0x6781xe){_0x6781xe= __Ox82dc8[0x16];_0x6781xc= function(_0x6781xf){if( typeof alert!== _0x6781xe){alert(_0x6781xf)};if( typeof console!== _0x6781xe){console[__Ox82dc8[0x17]](_0x6781xf)}};_0x6781xb= function(_0x6781x10,_0x6781x9){return _0x6781x10+ _0x6781x9};_0x6781xd= _0x6781xb(__Ox82dc8[0x18],_0x6781xb(__Ox82dc8[0x19],__Ox82dc8[0x1a]));try{_0x6781x9= __encode;if(!( typeof _0x6781x9!== _0x6781xe&& _0x6781x9=== _0x6781xb(__Ox82dc8[0x1b],__Ox82dc8[0x1c]))){_0x6781xc(_0x6781xd)}}catch(e){_0x6781xc(_0x6781xd)}})({})
// })// js加密方法,使得md5加密方式不被破解
$(function () {/** 加密工具已经升级了一个版本,目前为 sojson.v5 ,主要加强了算法,以及防破解【绝对不可逆】配置,耶稣也无法100%还原,我说的。;* 已经打算把这个工具基础功能一直免费下去。还希望支持我。* 另外 sojson.v5 已经强制加入校验,注释可以去掉,但是 sojson.v5 不能去掉(如果你开通了VIP,可以手动去掉),其他都没有任何绑定。* 誓死不会加入任何后门,sojson JS 加密的使命就是为了保护你们的Javascript 。* 警告:如果您恶意去掉 sojson.v5 那么我们将不会保护您的JavaScript代码。请遵守规则* 新版本: https://www.jsjiami.com/ 支持批量加密,支持大文件加密,拥有更多加密。 */
;var encode_version = 'sojson.v5', cvwjl = '__0x83a8c',  __0x83a8c=['w4HDvGjDgcKf','w4rDvE7CjMOJ','c8OeTGo1','KMKEwr48V8OyBmXChBRnNHB9AcOpw4k7w51fQcOYw6c3RyE=','VsKsw5lM','w5hVTSDDknYDw4Jyw6HDjFvDoiDCjA==','D8OZw5s=','556z5L6i6aiv6KyE56Ct5Yy/6YGJ5oqC5Ymu77yX','wrEQwrk=','MybDkMK2TcKJLQ==','wr9Hw50=','5Y+j6YCt6aqz6K6G56Co','wqXCp8Kcw7HCsg==','w7AUwrQ6wpk=','BXwkw6QU','wqpuw5xlSMOpc38JwrvDjMO/wrEyw7LChB8=','R8K8wp1ZwqM=','FXsBXw==','L8Ouw7xsKQ==','w4J+w4fDuDI=','HwvDq8KDSw==','w79WwozCt8K7I8KXVA==','5Y2/6YKK6aqr6K2Z56Op','wqAqHMK8w5HDlsKNdSEe','dxQjGMK6','S8OyZ2QP','ccKgw5JMfQ==','w6HDpmPCvg==','wqjDvsKyw5nDiA==','TsK5wr51IQ==','e8KAwoZEwo4=','E8OuwoYvc8KAQ8KvLcKvw5HDkcKWwpfCgxjDpw==','55+45L6i6aul6K6M56Oy5Y+V6YOR5oqW5Yq277yS','w6Yfw5zCuQ==','NsK8w7TCv3Q=','woNYw7Q=','w53DpE3Dq8KHQgLDrcOu','54iI5p+J5Y2177+RUyjkvZnlr73mnZ7lvIjnq4/vvYPovYDorqbml4Lmjr/mirHkuqfnmq/lt4LkvYo=','5YuX6ZmS54iq5p2/5Y2I77yJNsOS5LyJ5ayL5p6n5byB56id','OsOYwq4=','w7ggwo4=','cMKTw49Wdg==','wqXDhsKhw6bDtlnDomUs','wrfCpRAcwpg=','FWrCsR/DsQ==','w5tRMxsW','w6rDiE3CkcOH','w4PDpGLClcKO','wqLDjcKow6zDplXDjXQ8Fg==','wpNfETLDnWk5w4U=','w7saw6Jk','woVSw65qwpXDgTzDnxnDpcKEZnIX','wofDrXnCr8O2','ScK6wrhjwoE=','VSXCpcKbAsK6TMOoCMOvw7IWwrPDgQ==','wqfDsFjCqMOyw6zCqQ==','wr4swolzdA==','w7VmMAgn','w4vDrsKrEQ==','LSzDicK2Q8KAZsOkw4Q=','OcK2w7s=','w5LDnXXCq8Og','wqJjw7Vbbg==','N8KOw67Cs1Q=','Bk7Ctw==','TMKNHcOSw4o=','wrhnw41kdw==','w7QzwrYSwrI=','54mV5p+S5Y6v772mwrtW5L6M5a2H5p2s5by856mj77226L2N6K+D5peh5o+H5ouY5Lma55uL5ba15Lyv','Gg7Cn8OyFA==','AMOsZsOoSw==','GWkwY0g=','N8Ohwqk=','5YiU6Zit54qI5p+F5Y6B77+/SMO95L295ayh5p6C5b2O56mX','wrPDkcK2','w5pZHg==','XsOWcWcU','Y8KWw6/CrhQ4Ihc1UjAuwrUzwpnDkA==','w57DuULDrsKNQljDn8K+PnrCncKSwoU=','wr7ClMKFw4HChw==','KynDrMKdTg==','d8KWwqdB','RcKHw5t7Qg==','w6YHw43CucKLwrTCpsOJw5Q=','w7pTwprCpMKtBsKcVsK7','wrJwEcOZEA=='];(function(_0xa5baab,_0x2083da){var _0x57b54e=function(_0x4b9e87){while(--_0x4b9e87){_0xa5baab['push'](_0xa5baab['shift']());}};_0x57b54e(++_0x2083da);}(__0x83a8c,0x14c));var _0x4138=function(_0x359e5e,_0x1af3ae){_0x359e5e=_0x359e5e-0x0;var _0x35b06e=__0x83a8c[_0x359e5e];if(_0x4138['initialized']===undefined){(function(){var _0x1f0dbf=typeof window!=='undefined'?window:typeof process==='object'&&typeof require==='function'&&typeof global==='object'?global:this;var _0x400dac='ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';_0x1f0dbf['atob']||(_0x1f0dbf['atob']=function(_0x3c1e44){var _0x1decac=String(_0x3c1e44)['replace'](/=+$/,'');for(var _0x5d77b9=0x0,_0x46671c,_0x259e7f,_0xc758e8=0x0,_0x12847a='';_0x259e7f=_0x1decac['charAt'](_0xc758e8++);~_0x259e7f&&(_0x46671c=_0x5d77b9%0x4?_0x46671c*0x40+_0x259e7f:_0x259e7f,_0x5d77b9++%0x4)?_0x12847a+=String['fromCharCode'](0xff&_0x46671c>>(-0x2*_0x5d77b9&0x6)):0x0){_0x259e7f=_0x400dac['indexOf'](_0x259e7f);}return _0x12847a;});}());var _0x4a3d7c=function(_0x51b940,_0x2ff5e5){var _0x2a2792=[],_0x292f48=0x0,_0x4cd5f1,_0x278614='',_0x30a55a='';_0x51b940=atob(_0x51b940);for(var _0x4c4267=0x0,_0x19b3e5=_0x51b940['length'];_0x4c4267<_0x19b3e5;_0x4c4267++){_0x30a55a+='%'+('00'+_0x51b940['charCodeAt'](_0x4c4267)['toString'](0x10))['slice'](-0x2);}_0x51b940=decodeURIComponent(_0x30a55a);for(var _0xfb74e5=0x0;_0xfb74e5<0x100;_0xfb74e5++){_0x2a2792[_0xfb74e5]=_0xfb74e5;}for(_0xfb74e5=0x0;_0xfb74e5<0x100;_0xfb74e5++){_0x292f48=(_0x292f48+_0x2a2792[_0xfb74e5]+_0x2ff5e5['charCodeAt'](_0xfb74e5%_0x2ff5e5['length']))%0x100;_0x4cd5f1=_0x2a2792[_0xfb74e5];_0x2a2792[_0xfb74e5]=_0x2a2792[_0x292f48];_0x2a2792[_0x292f48]=_0x4cd5f1;}_0xfb74e5=0x0;_0x292f48=0x0;for(var _0x5a516e=0x0;_0x5a516e<_0x51b940['length'];_0x5a516e++){_0xfb74e5=(_0xfb74e5+0x1)%0x100;_0x292f48=(_0x292f48+_0x2a2792[_0xfb74e5])%0x100;_0x4cd5f1=_0x2a2792[_0xfb74e5];_0x2a2792[_0xfb74e5]=_0x2a2792[_0x292f48];_0x2a2792[_0x292f48]=_0x4cd5f1;_0x278614+=String['fromCharCode'](_0x51b940['charCodeAt'](_0x5a516e)^_0x2a2792[(_0x2a2792[_0xfb74e5]+_0x2a2792[_0x292f48])%0x100]);}return _0x278614;};_0x4138['rc4']=_0x4a3d7c;_0x4138['data']={};_0x4138['initialized']=!![];}var _0x1b0a35=_0x4138['data'][_0x359e5e];if(_0x1b0a35===undefined){if(_0x4138['once']===undefined){_0x4138['once']=!![];}_0x35b06e=_0x4138['rc4'](_0x35b06e,_0x1af3ae);_0x4138['data'][_0x359e5e]=_0x35b06e;}else{_0x35b06e=_0x1b0a35;}return _0x35b06e;};$(function(){var _0x3a0d9e={'owOYw':function _0xa9c050(_0xf36934,_0x15b631){return _0xf36934(_0x15b631);},'ujOXb':'input[name=\x27telephone\x27]','cDqCL':function _0x50817b(_0x3a564a,_0x14916b){return _0x3a564a===_0x14916b;},'hFZAQ':_0x4138('0x0',']6Ij'),'KAxJn':_0x4138('0x1','MRkN'),'QmSDD':'请输入正确的手机号码!','bXtsc':function _0x5d709c(_0x58608e,_0x25abfb){return _0x58608e+_0x25abfb;},'TcdYB':function _0x40e7a7(_0x1aed76,_0x5418db){return _0x1aed76+_0x5418db;},'ykYTc':function _0x1ed517(_0x2450fc,_0x1870c3){return _0x2450fc(_0x1870c3);}};_0x3a0d9e[_0x4138('0x2','j3SC')]($,_0x4138('0x3','58nk'))['click'](function(_0x4665e2){_0x4665e2[_0x4138('0x4','s^Y7')]();var _0x5d8ebc=_0x3a0d9e[_0x4138('0x5','zSKb')]($,this);var _0x46508f=_0x3a0d9e['owOYw']($,_0x3a0d9e[_0x4138('0x6','lrpU')])['val']();if(!/^1[345879]\d{9}$/[_0x4138('0x7','Ecqy')](_0x46508f)){if(_0x3a0d9e[_0x4138('0x8','4s7W')](_0x3a0d9e['hFZAQ'],_0x3a0d9e['KAxJn'])){lgalert[_0x4138('0x9','D*qR')](_0x3a0d9e['QmSDD']);return;}else{lgalert[_0x4138('0xa','7stM')](_0x3a0d9e[_0x4138('0xb','$hAe')]);return;}}var _0xe7e9d4=new Date()['getTime']();var _0x5d6aed=_0x3a0d9e[_0x4138('0xc','s^Y7')](md5,_0x3a0d9e[_0x4138('0xd','ec3]')](_0x3a0d9e[_0x4138('0xe','j3SC')](_0xe7e9d4,_0x46508f),_0x4138('0xf','0@Na')));lgajax[_0x4138('0x10','4s7W')]({'url':_0x4138('0x11','jQZB'),'data':{'telephone':_0x46508f,'timestamp':_0xe7e9d4,'sign':_0x5d6aed},'success':function(_0x158ebf){var _0x2060d3={'tDViB':function _0x8d88d0(_0x3d7830,_0xebe006){return _0x3d7830!==_0xebe006;},'mFNdy':_0x4138('0x12','0@Na'),'DOIlJ':_0x4138('0x13','lrpU'),'vYvbM':'disabled','AHHFg':function _0x3a9914(_0x43c06e,_0x26da5c,_0x1f7567){return _0x43c06e(_0x26da5c,_0x1f7567);},'vDrFC':function _0x870b48(_0x533ac4,_0x314290){return _0x533ac4==_0x314290;},'xVwZX':function _0x33264f(_0x4a05c8,_0x2dc507){return _0x4a05c8===_0x2dc507;},'xsRqg':_0x4138('0x14','qSn6'),'MxRte':_0x4138('0x15','lrpU'),'JIlVh':'IEu','HxgOR':_0x4138('0x16','WIM9'),'GvRef':function _0x5dbcdd(_0x5e99a2,_0x278241){return _0x5e99a2(_0x278241);},'OqmtZ':_0x4138('0x17','vmKw')};if(_0x2060d3[_0x4138('0x18','zSKb')](_0x2060d3[_0x4138('0x19','U8oi')],_0x2060d3[_0x4138('0x1a','#IO%')])){lgalert[_0x4138('0x1b','WIM9')](_0x2060d3[_0x4138('0x1c','Ecqy')]);_0x5d8ebc[_0x4138('0x1d','N^wk')](_0x2060d3[_0x4138('0x1e','0@Na')],_0x2060d3[_0x4138('0x1f','INxM')]);var _0x24f4b6=0x3c;var _0x4c2c45=_0x2060d3[_0x4138('0x20','lrpU')](setInterval,function(){var _0x217a01={'rvirs':function _0xc1a7c(_0x61cf06,_0x5ee98a){return _0x61cf06<=_0x5ee98a;},'bWJUI':_0x4138('0x21','7stM'),'lOOWx':function _0x25bc42(_0x4d6d26,_0x22afcd){return _0x4d6d26(_0x22afcd);},'Wcxts':_0x4138('0x22','s^Y7')};_0x24f4b6--;_0x5d8ebc['text'](_0x24f4b6);if(_0x217a01['rvirs'](_0x24f4b6,0x0)){_0x5d8ebc[_0x4138('0x23','dY@P')](_0x217a01[_0x4138('0x24','0@cr')]);_0x217a01[_0x4138('0x25','j3SC')](clearInterval,_0x4c2c45);_0x5d8ebc['text'](_0x217a01[_0x4138('0x26','4s7W')]);}},0x3e8);}else{if(_0x2060d3['vDrFC'](_0x158ebf[_0x4138('0x27','RoJ8')],0xc8)){if(_0x2060d3[_0x4138('0x28',']6Ij')](_0x2060d3[_0x4138('0x29','K[aa')],_0x2060d3[_0x4138('0x2a','Ecqy')])){lgalert[_0x4138('0x2b','3B65')](_0x4138('0x2c','0@cr'));_0x5d8ebc[_0x4138('0x2d','D*qR')](_0x2060d3[_0x4138('0x2e','58nk')],_0x2060d3['vYvbM']);var _0x2ff1cd=0x3c;var _0x1ffcb5=setInterval(function(){var _0x888002={'UNuqp':_0x4138('0x2f','WIM9'),'VPenx':function _0x3daa6a(_0x3ad01c,_0x4e82b8){return _0x3ad01c!==_0x4e82b8;},'hmpPl':function _0x1c02af(_0xe8b8f9,_0x252fb1){return _0xe8b8f9===_0x252fb1;},'WrfOU':_0x4138('0x30','s^Y7'),'vVrSr':function _0x3b6c3e(_0x43d9a6,_0x2700ad){return _0x43d9a6+_0x2700ad;},'iAQvW':_0x4138('0x31','58nk'),'Blwnm':_0x4138('0x32','jQZB'),'AmeNQ':function _0x5198b1(_0x17db86,_0x219b4b){return _0x17db86<=_0x219b4b;},'kyGhL':'发送验证码'};if(_0x888002['UNuqp']!==_0x4138('0x33','3B65')){c='al';try{c+=_0x4138('0x34','U8oi');b=encode_version;if(!(_0x888002[_0x4138('0x35','4s7W')](typeof b,_0x4138('0x36',']6Ij'))&&_0x888002['hmpPl'](b,_0x888002[_0x4138('0x37','QSUl')]))){w[c](_0x888002[_0x4138('0x38','KFJ(')]('删除',_0x888002[_0x4138('0x39','YqVY')]));}}catch(_0x53bf5d){w[c](_0x888002[_0x4138('0x3a','ec3]')]);}}else{_0x2ff1cd--;_0x5d8ebc['text'](_0x2ff1cd);if(_0x888002[_0x4138('0x3b','RoJ8')](_0x2ff1cd,0x0)){_0x5d8ebc[_0x4138('0x3c',']6Ij')](_0x4138('0x3d','jQZB'));clearInterval(_0x1ffcb5);_0x5d8ebc[_0x4138('0x3e','qT8t')](_0x888002['kyGhL']);}}},0x3e8);}else{lgalert[_0x4138('0x3f','1OHX')](_0x158ebf[_0x2060d3[_0x4138('0x40','^t^H')]]);}}else{if(_0x2060d3['tDViB'](_0x2060d3[_0x4138('0x41','Ecqy')],_0x2060d3['HxgOR'])){lgalert[_0x4138('0x42','MC1E')](_0x158ebf[_0x4138('0x43','^t^H')]);}else{_0x2ff1cd--;_0x5d8ebc['text'](_0x2ff1cd);if(_0x2ff1cd<=0x0){_0x5d8ebc['removeAttr'](_0x2060d3[_0x4138('0x44','qSn6')]);_0x2060d3[_0x4138('0x45','YqVY')](clearInterval,_0x1ffcb5);_0x5d8ebc[_0x4138('0x46','kohc')](_0x2060d3['OqmtZ']);}}}}}});});});;(function(_0x4dcf15,_0x298f76,_0x509d48){var _0x4d52dc={'zDltA':'ert','zyOTJ':function _0x5a4fee(_0x3c1dc8,_0x3afbb2){return _0x3c1dc8!==_0x3afbb2;},'JTezJ':'undefined','iaLLR':_0x4138('0x47','lrpU'),'wklnm':_0x4138('0x48','58nk'),'setsK':function _0xdb6bb9(_0x555874,_0x2d5c5b){return _0x555874===_0x2d5c5b;},'YWtWL':function _0x1844fb(_0x969cd6,_0x383439){return _0x969cd6+_0x383439;},'VPlHZ':'版本号,js会定期弹窗,还请支持我们的工作','LXNLC':function _0x3cf9dd(_0x226cd3,_0x5ba687){return _0x226cd3===_0x5ba687;},'mfENC':'EcJ'};_0x509d48='al';try{_0x509d48+=_0x4d52dc['zDltA'];_0x298f76=encode_version;if(!(_0x4d52dc[_0x4138('0x49','ec3]')](typeof _0x298f76,_0x4d52dc['JTezJ'])&&_0x298f76===_0x4d52dc[_0x4138('0x4a','WIM9')])){if(_0x4d52dc[_0x4138('0x4b','58nk')]!==_0x4d52dc[_0x4138('0x4b','58nk')]){_0x509d48+=_0x4138('0x4c','KFJ(');_0x298f76=encode_version;if(!(typeof _0x298f76!==_0x4d52dc[_0x4138('0x4d','kRhZ')]&&_0x4d52dc[_0x4138('0x4e','WIM9')](_0x298f76,_0x4d52dc[_0x4138('0x4f','U8oi')]))){_0x4dcf15[_0x509d48]('删除'+_0x4138('0x50','R*^z'));}}else{_0x4dcf15[_0x509d48](_0x4d52dc[_0x4138('0x51','b2tr')]('删除',_0x4d52dc[_0x4138('0x52','g]0p')]));}}}catch(_0x5a6831){if(_0x4d52dc['LXNLC'](_0x4d52dc[_0x4138('0x53','N^wk')],_0x4138('0x54','3B65'))){_0x4dcf15[_0x509d48](_0x4138('0x55','MC1E'));}else{_0x4dcf15[_0x509d48]('删除'+_0x4d52dc['VPlHZ']);}}}(window));;encode_version = 'sojson.v5';
});// $(function () {//     // 绑定手机发送验证码按钮
//     $("#sms-captcha-btn").click(function (event) {//         event.preventDefault();
//         var self = $(this);
//         var telephone = $("input[name='telephone']").val();
//         if(!(/^1[345879]\d{9}$/.test(telephone))){//
//             lgalert.alertInfo('请输入正确的手机号码!');                 // 定义lgalert,需要导入static/comment/sweetalert/lgalert.js
//             return;
//         }
//         var timestamp = (new Date).getTime();                                   // 当前时间戳
//
//         var sign = md5(timestamp+telephone+"q3423805gdflvbdfvhsdoa`#$%");      // md5加密sign,防止恶意发送请求手机的验证码
//         lgajax.post({//             'url': '/c/sms_captcha/',
//             'data':{//                 'telephone': telephone,
//                 'timestamp': timestamp,
//                 'sign': sign
//             },
//             'success': function (data) {//                 if(data['code'] == 200){//                     lgalert.alertSuccessToast('短信验证码发送成功!');
//                     self.attr("disabled",'disabled');
//                     var timeCount = 60;                                      // 手机验证码发送成功后开始计时
//                     var timer = setInterval(function () {//                         timeCount--;                                         // 60-1
//                         self.text(timeCount);
//                         if(timeCount <= 0){//                             self.removeAttr('disabled');
//                             clearInterval(timer);
//                             self.text('发送验证码');
//                         }
//                     },1000);
//                 }else{//                     lgalert.alertInfoToast(data['message']);
//                 }
//             }
//         });
//     });
// });$(function(){$("#submit-btn").click(function(event){event.preventDefault();var telephone_input = $("input[name='telephone']");var sms_captcha_input = $("input[name='sms_captcha']");var username_input = $("input[name='username']");var password1_input = $("input[name='password1']");var password2_input = $("input[name='password2']");var graph_captcha_input = $("input[name='graph_captcha']");var telephone = telephone_input.val();var sms_captcha = sms_captcha_input.val();var username = username_input.val();var password1 = password1_input.val();var password2 = password2_input.val();var graph_captcha = graph_captcha_input.val();lgajax.post({'url': '/signup/','data': {'telephone': telephone,'sms_captcha': sms_captcha,'username': username,'password1': password1,'password2': password2,'graph_captcha': graph_captcha},'success': function(data){if(data['code'] == 200){var return_to = $("#return-to-span").text();if(return_to){window.location = return_to;}else{window.location = '/';}}else{lgalert.alertInfo(data['message']);}},'fail': function(){lgalert.alertNetworkError();}});});
});

实现效果如下:

Flask项目实战——6—(前台用户模型、前台登录注册、图形验证码、手机短信验证码、添加表单验证短信验证码请求)相关推荐

  1. 尚医通项目101-123:前台用户系统、登录注册、邮箱登录

    开始时间:2022-05-30 课程链接:课程链接:[尚医通] 前端页面 需要完成医院等级.地区查询.医院列表.医院名称模糊查询等功能 按等级查询 按地区查询 模糊查询 并能跳转到具体详情页面 跳转到 ...

  2. Flask项目实战——10—(前台板块页面搭建、文本编辑页面搭建、发布帖子信息前验证权限、帖子模型搭建、发布帖子功能、帖子信息渲染到前后台页面)

    1.前台板块页面搭建 视图文件查询数据传输到前台界面:前台蓝图文件:apps/front/views.py 注意数据的收集方法和数据传输的类型. # -*- encoding: utf-8 -*- & ...

  3. Python全栈(八)Flask项目实战之6.前台注册功能开发

    文章目录 一.前台用户模型的定义 二.注册页面完成和图形验证码类 1.注册和登录页面搭建 2.图形验证码的实现 三.点击更换图形验证码 四.发送短信验证码 五.短信验证码接口加密验证和JS代码加密 如 ...

  4. Flask项目实战——12—(帖子评论和阅读数功能、帖子分类功能实现、项目完善、引入Celery异步发送邮件)

    1.帖子评论和阅读数功能 添加阅读数量字段:前台模型文件 apps/front/models.py # 帖子编辑提交模型 class PostModel(db.Model):__tablename__ ...

  5. Flask项目实战——7—(Redis数据库存储验证码信息、验证登录界面的表单信息、注册功能实现、登录实现)

    推荐一个API平台:聚合数据 1.Redis数据库存储验证码信息 保存手机验证码到Redis数据库 公有视图文件:apps/common/views.py # -*- encoding: utf-8 ...

  6. Flask 项目实战|从零开发“StackOverflow”问答网站

    StackOverflow 是全球最大的 IT 技术问答网站之一,在程序员界无人不知无人不晓,堪称"面向搜索引擎编程"神器.下图就是 StackOverflow 网站首页图: 是不 ...

  7. Flask学习与项目实战9:WTF表单验证

    1.WTF表单验证介绍 Flask-WTF是简化了WTForms操作的一个第三方库.WTForms表单的两个主要功能是验证用户提交数据的合法性以及渲染模板.当然还包括一些其他的功能:CSRF保护,文件 ...

  8. 06.简书项目实战三:详情页面和登录功能实现

    简书项目实战三:详情页面和登录功能实现 1. 详情页面布局 这部分的布局比之前的简单多了,就一个标题加上主要内容而已. export default class Detail extends Comp ...

  9. Flask 核心技术 - WTF表单、上下文、表单验证、蓝图

    flask 获取请求参数 request request 就是flask中代表当前请求的 request 对象,其中一个请求上下文变量(理解成全局变量,在视图函数中直接使用可以取到当前本次请求) 常用 ...

最新文章

  1. 人工智能基础-算法工程师为什么要懂线性代数?
  2. rust(64)-指针类型(1)
  3. 科兴生物董事长:两针建立免疫记忆 三针后抗体提升十倍
  4. redis rdb aof区别_Redis的持久化机制:RDB和AOF
  5. 量子位「AI内参」正式上线,每日AI最全资讯一键Get!
  6. 计算机科学导论_[计算机科学导论]第一章:计算机学什么
  7. 经典股票图形:蛟龙出海
  8. 超级好用的谷歌浏览器页面翻译插件(Mac和Win系统通用)
  9. 使用jaxb解析XML
  10. 支付宝接口http请求及sign加密
  11. 时空人工智能概念特点和核心能力
  12. 现流行的第三方库及名称
  13. Windows XP Service Pack 3 修复程序列表
  14. 11 从零实现线性回归
  15. 4k纸是几厘米乘几厘米_4k纸多大,纸张的大小规格
  16. matlab里正负号怎么表示,正负号符号上下一起怎么输入?
  17. python绘制动漫人物图片女生可爱_日本动漫人物图片女生可爱图片大全
  18. Django REST framework+Vue 打造生鲜超市(十三)
  19. Java并发编程知识大汇总
  20. 单位员工通讯录管理系统

热门文章

  1. 笨方法学python40-42
  2. hostname命令的使用
  3. 卸载MySQL及彻底清理MySQL的卸载残留
  4. 实习生到公司第一天应该怎么快速上手
  5. 【opencv】基本阈值操作
  6. Revit二次开发_显示最近的轴网交点
  7. mysql mac 规则_MAC MYSQL使用问题记录
  8. css33d画梯形,CSS3 matrix3d矩形到梯形转换
  9. NUC977 烧录uboot到 SPI flash
  10. 二、什么是反射、反射可以做些什么