Layui + Flask 实现注册、登录功能(案例篇)

2023-06-1414:58:21后端程序开发Comments1,616 views字数 14589阅读模式

看了 layui 表单相关的知识,接下来就可以实现注册功能,功能逻辑如下:文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

Layui + Flask 实现注册、登录功能(案例篇)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

项目创建

  1. 新建 flask 项目
  2. 下载 layui 文件,解压之后复制到指定文件
  3. 编写前端首页、注册页、登录页。注册页使用之前看过的注册页表单
  4. 编写后端路由,实现前后端基本的跳转

新建 flask 项目

新建项目的方式有很多种,只要有任意一种方式创建即刻。在这里我直接选择使用 pycharm 进行创建。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

新建成功后目录如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

D:\flask-register>tree /fD:.│  app.py├─static└─templates

下载 layui 文件

访问 https://layui.dev/ ,点击直接下载之后就能得到 layui 的静态文件,将其复制到 static 目录下,完成之后的目录结构如下文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

D:\flask-register>tree /f卷 软件 的文件夹 PATH 列表卷序列号为 65F3-3762D:.│  app.py├─static│  │  layui.js│  ││  ├─css│  │      layui.css│  ││  └─font│          iconfont.eot│          iconfont.svg│          iconfont.ttf│          iconfont.woff│          iconfont.woff2└─templates

编写静态页面

  • 复制 https://layui.dev/docs/2.8/layout/ 的内容进行修改。完成 index.html (首页)的制作。
  • 复制 https://layui.dev/docs/2.8/form/#reg 修改为注册页
  • 复制 https://layui.dev/docs/2.8/form/#login 修改为登录页

index.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>首页</title>    <link rel="stylesheet" href="/static/css/layui.css"></head><body><div class="layui-layout layui-layout-admin">    <div class="layui-header">        <div class="layui-logo layui-hide-xs layui-bg-black">正心全栈编程</div>        <ul class="layui-nav layui-layout-left">            <li class="layui-nav-item"><a href="">最新活动</a></li>            <li class="layui-nav-item layui-this"><a href="">核心编程</a></li>            <li class="layui-nav-item"><a href="">爬虫开发</a></li>            <li class="layui-nav-item"><a href="">数据分析</a></li>
            <li class="layui-nav-item">                <a href="JavaScript:;">网站开发</a>                <dl class="layui-nav-child">                    <dd><a href="">前端基础</a></dd>                    <dd><a href="">flask 框架</a></dd>                    <dd><a href="">flask 项目</a></dd>                </dl>            </li>        </ul>
        <ul class="layui-nav layui-layout-right">            <li class="layui-nav-item layui-hide layui-show-sm-inline-block">                <a href="javascript:;">                    <img src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png" class="layui-nav-img">                    登录 / 正心                </a>                <dl class="layui-nav-child">                    <dd><a href="javascript:;">个人中心</a></dd>                    <dd><a href="javascript:;">设置</a></dd>                    <dd><a href="javascript:;">退出</a></dd>                </dl>            </li>        </ul>    </div></div><script src="/static/layui.js"></script></body></html>

register.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>注册页</title>    <link rel="stylesheet" href="/static/css/layui.css"></head><body><style>    .demo-reg-container {        width: 320px;        margin: 21px auto 0;    }
    .demo-reg-other .layui-icon {        position: relative;        display: inline-block;        margin: 0 2px;        top: 2px;        font-size: 26px;    }</style><form class="layui-form" lay-filter="register-form">    <div class="demo-reg-container">        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-username"></i>                </div>                <input type="text" name="nickname" value="" lay-verify="required" placeholder="昵称" autocomplete="off"                       class="layui-input" lay-affix="clear">            </div>        </div>        <div class="layui-form-item">            <div class="layui-row">                <div class="layui-col-xs7">                    <div class="layui-input-wrap">                        <div class="layui-input-prefix">                            <i class="layui-icon layui-icon-cellphone"></i>                        </div>                        <input type="text" name="mobile" value="" lay-verify="required|phone" placeholder="手机号"                               lay-reqtext="请填写手机号" autocomplete="off" class="layui-input" id="reg-cellphone">                    </div>                </div>                <div class="layui-col-xs5">                    <div style="margin-left: 11px;">                        <button type="button" class="layui-btn layui-btn-fluid layui-btn-primary"                                lay-on="reg-get-vercode">获取验证码                        </button>                    </div>                </div>            </div>        </div>        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-vercode"></i>                </div>                <input type="text" name="vercode" value="" lay-verify="required" placeholder="验证码"                       lay-reqtext="请填写验证码" autocomplete="off" class="layui-input">            </div>        </div>        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-password"></i>                </div>                <input type="password" name="password" value="" lay-verify="required" placeholder="密码"                       autocomplete="off" class="layui-input" id="reg-password" lay-affix="eye">            </div>        </div>        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-password"></i>                </div>                <input type="password" name="confirmPassword" value="" lay-verify="required|confirmPassword"                       placeholder="确认密码" autocomplete="off" class="layui-input" lay-affix="eye">            </div>        </div>
        <div class="layui-form-item">            <input type="checkbox" name="agreement" lay-verify="required" lay-skin="primary" title="同意">            <a href="#terms" target="_blank" style="position: relative; top: 6px; left: -15px;">                <ins>用户协议</ins>            </a>        </div>        <div class="layui-form-item">            <button class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo-reg">注册</button>        </div>        <div class="layui-form-item demo-reg-other">            <label>社交账号注册</label>            <span style="padding: 0 21px 0 6px;">        <a href="javascript:;"><i class="layui-icon layui-icon-login-qq" style="color: #3492ed;"></i></a>        <a href="javascript:;"><i class="layui-icon layui-icon-login-wechat" style="color: #4daf29;"></i></a>        <a href="javascript:;"><i class="layui-icon layui-icon-login-weibo" style="color: #cf1900;"></i></a>      </span>            <a href="/login">登录已有帐号</a>        </div>    </div></form><script src="/static/layui.js"></script><script>    layui.use(function () {        var $ = layui.$;        var form = layui.form;        var layer = layui.layer;    });</script></body></html>

login.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <title>登录页</title>    <link rel="stylesheet" href="/static/css/layui.css"></head><body><style>    .demo-login-container {        width: 320px;        margin: 21px auto 0;    }
    .demo-login-other .layui-icon {        position: relative;        display: inline-block;        margin: 0 2px;        top: 2px;        font-size: 26px;    }</style><form class="layui-form">    <div class="demo-login-container">        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-cellphone"></i>                </div>                <input type="text" name="mobile" value="" lay-verify="required" placeholder="手机号"                       lay-reqtext="请填写手机号" autocomplete="off" class="layui-input" lay-affix="clear">            </div>        </div>        <div class="layui-form-item">            <div class="layui-input-wrap">                <div class="layui-input-prefix">                    <i class="layui-icon layui-icon-password"></i>                </div>                <input type="password" name="password" value="" lay-verify="required" placeholder="密   码"                       lay-reqtext="请填写密码" autocomplete="off" class="layui-input" lay-affix="eye">            </div>        </div>        <div class="layui-form-item">            <div class="layui-row">                <div class="layui-col-xs7">                    <div class="layui-input-wrap">                        <div class="layui-input-prefix">                            <i class="layui-icon layui-icon-vercode"></i>                        </div>                        <input type="text" name="captcha" value="" lay-verify="required" placeholder="验证码"                               lay-reqtext="请填写验证码" autocomplete="off" class="layui-input" lay-affix="clear">                    </div>                </div>                <div class="layui-col-xs5">                    <div style="margin-left: 10px;">                        <img src="https://www.oschina.net/action/user/captcha"                             id="captchaImage">                    </div>                </div>            </div>        </div>        <div class="layui-form-item">            <input type="checkbox" name="remember" lay-skin="primary" title="记住密码">            <a href="#forget" style="float: right; margin-top: 7px;">忘记密码?</a>        </div>        <div class="layui-form-item">            <button class="layui-btn layui-btn-fluid" lay-submit lay-filter="demo-login">登录</button>        </div>        <div class="layui-form-item demo-login-other">            <label>社交账号登录</label>            <span style="padding: 0 21px 0 6px;">                <a href="javascript:;"><i class="layui-icon layui-icon-login-qq" style="color: #3492ed;"></i></a>                <a href="javascript:;"><i class="layui-icon layui-icon-login-wechat" style="color: #4daf29;"></i></a>                <a href="javascript:;"><i class="layui-icon layui-icon-login-weibo" style="color: #cf1900;"></i></a>              </span><a href="/register">注册帐号</a>        </div>    </div></form><script src="/static/layui.js"></script><script>    layui.use(function () {        var form = layui.form;        var layer = layui.layer;        // 提交事件        form.on('submit(demo-login)', function (data) {            var field = data.field; // 获取表单字段值            // 显示填写结果,仅作演示用            layer.alert(JSON.stringify(field), {                title: '当前填写的字段值'            });            // 此处可执行 Ajax 等操作            // …            return false; // 阻止默认 form 跳转        });    });</script></body></html>

后端适配

前端编写好了之后,需要适配后端路由才能实现访问。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

from flask import Flask, render_template
app = Flask(__name__)

@app.route('/')def index_view():    return render_template('index.html')

@app.route('/register')def register_view():    return render_template('register.html')

@app.route('/login')def login_view():    return render_template('login.html')

if __name__ == '__main__':    app.run(debug=True)

后端路由编写好了之后,如果想要实现联调,就需要修改前端页面的逻辑。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

  1. 没有登录之前不显示个人信息,只显示左上角的登录按钮,点击登录按钮调整到登录页面。
  2. 登录是没有账号,就先进行注册。注册页面如果有账号就跳转到登录页面。
  3. 注册账号成功之后跳转到登录页面

实现注册逻辑

注册逻辑观看最开始的时序图。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

在实现逻辑之前,先完善一下前端跳转页面文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

1、没有登录之前不显示个人信息,只显示左上角的登录按钮,修改 index.html 的内容。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<ul class="layui-nav layui-layout-right">    <li class="layui-nav-item layui-hide layui-show-sm-inline-block">        {% if not is_login %}        <a href="/login">            登录        </a>        {% else %}        <a href="javascript:;">            <img src="//unpkg.com/outeres@0.0.10/img/layui/icon-v2.png" class="layui-nav-img">            登录 / 正心        </a>        <dl class="layui-nav-child">            <dd><a href="javascript:;">个人中心</a></dd>            <dd><a href="javascript:;">设置</a></dd>            <dd><a href="javascript:;">退出</a></dd>        </dl>        {% endif %}    </li></ul>

2、登录是没有账号,就先进行注册。注册页面如果有账号就跳转到登录页面。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

修改 login.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<a href="/register">注册帐号</a>

修改 register.html文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<a href="/login">登录已有帐号</a>

前端校验手机号

用户输入昵称、手机号码,点击获取验证码之前前端需要校验格式是否符合。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

发送短信接口请看附录文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

1、输入手机号之后进行校验文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<script>    layui.use(function () {        var $ = layui.$;        var form = layui.form;        var layer = layui.layer;        var util = layui.util;
        // 普通事件        util.on('lay-on', {            // 获取验证码            'reg-get-vercode': function (othis) {                var isvalid = form.validate('#reg-cellphone'); // 主动触发验证,v2.7.0 新增                // 验证通过                if (isvalid) {                    {                        #layer.msg('手机号规则验证通过');                    #                    }                    // 此处可继续书写「发送验证码」等后续逻辑                    let data = form.val('register-form');                    // 发送请求                    fetch('/api/send_register_sms', {                        method: 'POST',                        headers: {'Content-Type': 'application/json'},                        body: JSON.stringify({'mobile': data.mobile})                    }).then(response => response.json()).then(data => {                        // 处理后端的响应数据                        if (!data.code) {                            layer.msg(data.message, {icon: 1})                        } else {                            layer.msg(data.message, {icon: 2})                        }                    })                }            }        });    });</script>

点击获取验证码之后,就会发送请求到后端,然后后端处理完之后返回结果给前端。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

后端发送短信验证码

后端获取数据之后文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

  1. 解析前端传递过来的手机号,然后进行校验
  2. 调用第三方工具下发注册短信验证码(腾讯云、阿里云就有对于的服务)
  3. 将手机号、短信验证码记录到数据库。下次提交时再进行校验
import loggingimport reimport randomfrom flask import Flask, render_template, request, session
app = Flask(__name__)app.secret_key = 'notes.zhengxinonly.com'

@app.post('/api/send_register_sms')def send_register_sms():    # 1. 解析前端传递过来的数据    data = request.get_json()    mobile = data['mobile']
    # 2. 校验手机号码    pattern = r'^1[3-9]\d{9}$'    ret = re.match(pattern, mobile)    if not ret:        return {            'message': '电话号码不符合格式',            'code': -1        }
    # 3. 发送短信验证码,并记录    session['mobile'] = mobile    # 3.1 生成随机验证码    code = random.choices('123456789', k=6)    session['code'] = ''.join(code)    logging.warning(code)    return {        'message': '发送短信成功',        'code': 0    }

因为是一个简单的案例,就没有调用腾讯云短信进行测试了,而是直接把正确结果返回。对于短信与验证码也没有存储到数据库了,而是放在 session 里面进行返回,下次请求注册时,再从里面进行提取。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

实现注册逻辑

前端部分

在手机收到短信验证码之后,前端填入验证码,然后再输入两次密码,点击同意协议按钮,就能实现注册。在这同时需要做验证验证码与密码。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<script>    layui.use(function () {        var $ = layui.$;        var form = layui.form;        var layer = layui.layer;        var util = layui.util;
        // 自定义验证规则        form.verify({            // 确认密码            confirmPassword: function (value, item) {                var passwordValue = $('#reg-password').val();                if (value !== passwordValue) {                    return '两次密码输入不一致';                }            }        });
        // 提交事件        form.on('submit(demo-reg)', function (data) {            var field = data.field; // 获取表单字段值
            // 是否勾选同意            if (!field.agreement) {                layer.msg('您必须勾选同意用户协议才能注册');                return false;            }
            // 此处可执行 Ajax 等操作            fetch('/api/register', {                method: 'POST',                headers: {'Content-Type': 'application/json'},                body: JSON.stringify(field)            }).then(response => response.json()).then(data => {                // 处理后端的响应数据                if (!data.code) {                    layer.msg(data.message, {icon: 1})                    setTimeout(function () {                        location.href = '/login'                    }, 2000)                } else {                    layer.msg(data.message, {icon: 2})                }            })
            return false; // 阻止默认 form 跳转        });    });</script>

后端逻辑

数据库文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

小案例,使用 sqlite 是最方便的。文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

db.py文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

import sqlite3

class Database:    def __init__(self):        self.conn = sqlite3.connect('flask-layui.sqlite')        self.cursor = self.conn.cursor()
    def create_table(self):        sql = """create table user            (                id       integer primary key,                nickname varchar(255),                mobile   varchar(50),                password varchar(50)            );            """        self.cursor.execute(sql)        self.conn.commit()
    def insert(self, nickname, mobile, password):        sql = 'insert into user(nickname, mobile, password) values (?, ?, ?);'        self.cursor.execute(sql, (nickname, mobile, password))        self.conn.commit()
    def search(self, mobile):        sql = 'select password from user where mobile=?;'        self.cursor.execute(sql, (mobile,))        return self.cursor.fetchone()

db = Database()
if __name__ == '__main__':    db.create_table()    # db.insert('正心全栈编程', '18675867241', '123456')    # ret = db.search('18675867241')    # print(ret)

实现登录逻辑

登录时序图文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

Layui + Flask 实现注册、登录功能(案例篇)文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

图片验证码

  1. 客户端请求服务器获得图片验证码
  2. 服务器返回图片,将生成的验证码保存到数据库
  3. 客户端携带验证码进行请求,服务器校验数据
  4. 登录成功之后跳转到首页,登录失败则重新登录

前端发送请求

生成 uuid 用于记录验证码文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<script>    layui.use(function () {        var $ = layui.$;        var captcha_uuid = '';
        // 点击按钮更新验证码        $('#captchaImage').click(function () {            // 浏览器要发起图片验证码请求/captcha_code?captcha_code_uuid=xxxxx            captcha_uuid = generateUUID();            document.getElementById('captchaImage').src = '/get_captcha?captcha_uuid=' + captcha_uuid;        });        captcha_uuid = generateUUID();        document.getElementById('captchaImage').src = '/get_captcha?captcha_uuid=' + captcha_uuid;    });</script>

图片验证码更新文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

<script>    layui.use(function () {        var form = layui.form;        var layer = layui.layer;        var $ = layui.$;        var captcha_uuid = '';
        // 点击按钮更新验证码        $('#captchaImage').click(function () {            // 浏览器要发起图片验证码请求/captcha_code?captcha_code_uuid=xxxxx            captcha_uuid = generateUUID();            document.getElementById('captchaImage').src = '/get_captcha?captcha_uuid=' + captcha_uuid;        });
        captcha_uuid = generateUUID();        document.getElementById('captchaImage').src = '/get_captcha?captcha_uuid=' + captcha_uuid;    });</script>

后端生成图片验证码

安装依赖文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

pip install captcha

然后编写后端的视图文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

get_captcha.py文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

# filename: /get_captcha.pyfrom io import BytesIOfrom random import choices
from captcha.image import ImageCaptchafrom flask import make_responsefrom PIL import Image

def gen_captcha(content="0123456789"):    """生成验证码"""    image = ImageCaptcha()    # 获取字符串    captcha_text = "".join(choices(content, k=4))    # 生成图像    captcha_image = Image.open(image.generate(captcha_text))    return captcha_text, captcha_image

# 生成验证码def get_captcha_code_and_content():    code, image = gen_captcha()    out = BytesIO()    image.save(out, "png")    out.seek(0)    content = out.read()  # 读取图片的二进制数据做成响应体    return code, content

if __name__ == '__main__':    code, content = get_captcha_code_and_content()    print(code, content)

app.py文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

from get_captcha import get_captcha_code_and_content

@app.get('/get_captcha')def get_captcha_view():    # 1. 获取参数    captcha_uuid = request.args.get("captcha_uuid")    # 2. 生成验证码    code, content = get_captcha_code_and_content()
    # 3. 记录数据到数据库(用session代替)    session['code'] = code    resp = make_response(content)  # 读取图片的二进制数据做成响应体    resp.content_type = "image/png"    # 4. 错误处理
    # 5. 响应返回    return resp

前端请求进行登录

<script>    layui.use(function () {        var form = layui.form;        var layer = layui.layer;        var captcha_uuid = '';
        // 提交事件        form.on('submit(demo-login)', function (data) {            var field = data.field; // 获取表单字段值            // 显示填写结果,仅作演示用            field['captcha_uuid'] = captcha_uuid
            // 此处可执行 Ajax 等操作            fetch('/api/login', {                method: 'POST',                headers: {'Content-Type': 'application/json'},                body: JSON.stringify(field)            }).then(response => response.json()).then(data => {                if (!data.code) {                    layer.msg(data.message, {icon: 1})                    setTimeout(function () {                        location.href = '/'                    }, 2000)                } else {                    layer.msg(data.message, {icon: 2})                }            })            return false; // 阻止默认 form 跳转        });    });</script>

后端验证登录

编写登录接口文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

@app.post('/api/login')def login_api():    data = request.get_json()    ret = Database().search(data['mobile'])    code = session['code']    if code != data['captcha']:        return {            'message': '验证码错误',            'code': -1        }    if not ret:        return {            'message': '用户不存在',            'code': -1        }    pwd = ret[0]    if pwd != data['password']:        return {            'message': '用户密码错误',            'code': -1        }    session['is_login'] = True  # 记录用户登录    return {        'message': '用户登录成功',        'code': 0    }

完善 index.html 渲染文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

@app.route('/')def index_view():    is_login = session.get('is_login')     return render_template('index.html', is_login=is_login)

附录:API

https://apifox.com/apidoc/shared-1705c3ba-b68b-4ae4-b635-eacc8f7949a9文章源自菜鸟学院-https://www.cainiaoxueyuan.com/bc/47031.html

  • 本站内容整理自互联网,仅提供信息存储空间服务,以方便学习之用。如对文章、图片、字体等版权有疑问,请在下方留言,管理员看到后,将第一时间进行处理。
  • 转载请务必保留本文链接:https://www.cainiaoxueyuan.com/bc/47031.html

Comment

匿名网友 填写信息

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定