flask实现用户注册和登录功能

flask实现用户注册和登录

通常,无论设计一个什么网页,都会有一个用户管理的功能,这就需要实现用户的注册和登录功能。这里,我们将以一个简单的示例来实现功能完全用户注册和登录的最小demo。

文件路径:

demo
    apps
        front
             __init__.py
             views.py
             forms.py
             models.py
             decorators.py
             hooks.py
    static
        ...
    templates
        ...
    config.py
    exts.py
    manage.py
    server.py

准备

  • config.py
import os


# 开启debug模式, 需要在pycharm的Edit Configurations中勾选FLASK_DEBUG
DEBUG = True

# 开启secret_key
SECRET_KEY = os.urandom(24)

# session保存的字段名,可随意设置
FRONT_USER_ID = "front_user_id"

# 配置MySQL数据库
DB_USERNAME = 用户名
DB_PASSWOED = 密码
DB_HOST = "127.0.0.1"
DB_PORT = "3306"
DB_NAME = 数据库名字
DB_URI = "mysql+pymysql://{username}:{password}@{host}:{port}/{db}?charset=utf8".format(
    username=DB_USERNAME, password=DB_PASSWOED, host=DB_HOST, port=DB_PORT, db=DB_NAME
)
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False
  • exts.py
from flask_sqlalchemy import SQLAlchemy


db = SQLAlchemy()
  • manage.py
from flask_script import Manager
from flask_migrate import Migrate, MigrateCommand
from server import create_app
from exts import db


app = create_app()

manager = Manager(app)
Migrate(app, db)


manager.add_command('db', MigrateCommand)


if __name__ == '__main__':
    manager.run()

  • server.py
from flask import Flask
import config
from apps.front import bp as front_bp
from exts import db
from flask_wtf import CSRFProtect


def create_app():
    app = Flask(__name__)
    app.config.from_object(config)

    app.register_blueprint(front_bp)

    db.init_app(app)

    CSRFProtect(app)

    return app


if __name__ == '__main__':
    app = create_app()
    app.run()

  • hooks.py
from .views import bp
import config
from flask import session, g, render_template
from .models import FrontUser


@bp.before_request
def before_request():
    if config.FRONT_USER_ID in session:
        user_id = session.get(config.FRONT_USER_ID)
        user = FrontUser.query.get(user_id)

        if user:
            g.front_user = user


@bp.errorhandler
def page_not_found():
    return render_template('front/front_404.html'), 404

用户注册

  1. 首先,我们要实现一个简单的注册页面
  • front_signup.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="csrf-token" content="{{ csrf_token() }}">
    <title>注册</title>
    <link rel="stylesheet" href="{{ url_for('static', filename='layui/css/layui.css') }}">

    <script src="http://cdn.bootcss.com/jquery/3.1.1/jquery.min.js"></script>
    <script src="https://cdn.bootcdn.net/ajax/libs/blueimp-md5/2.16.0/js/md5.min.js"></script>
</head>
<body>
    <div class="sign-box">
        <form action="" class="layui-form layui-form-pane sign-form">
            <h1>注  册</h1>
            <div class="layui-form-item">
                <label class="layui-form-label">用户名</label>
                <div class="layui-input-block">
                    <input type="text" class="layui-input" lay-verify="required" name="username" placeholder="请输入您的用户名...">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">密码</label>
                <div class="layui-input-block">
                    <input type="password" class="layui-input" lay-verify="required" name="password1" placeholder="请输入您的密码...">
                </div>
            </div>
            <div class="layui-form-item">
                <label class="layui-form-label">确认密码</label>
                <div class="layui-input-block">
                    <input type="password" class="layui-input" lay-verify="required" name="password2" placeholder="请再次却惹您的密码...">
                </div>
            </div>
            <div class="layui-form-item">
                <div class="layui-input-block">
                    <button class="layui-btn" lay-submit lay-filter="signup">立即注册</button>
                </div>
            </div>
            <div class="layui-form-item">
                已注册 <a href="{{ url_for('front.signin') }}" style="color: #0000FF">我要登录</a>
            </div>
        </form>
    </div>
    <script src="{{ url_for('static', filename='layui/layui.js') }}"></script>
    <script>
        layui.use(['form'], function () {
            var form = layui.form;

            // 监听提交
            form.on('submit(signup)', function (data) {
                // data.field 为表单数据,以 key: value 的形式展现
                console.log(data.field);

                var username= data.field.username;
                var password1 = data.field.password1;
                var password2 = data.field.password2;

                $.ajax({
                    'method': 'post',
                    'url': '/signup/',
                    'data': {
                        'username': username,
                        'password1': password1,
                        'password2': password2,
                    },
                    'success': function (data) {
                        if(data['code'] === 200) {
                            window.location = '/';
                        } else {
                            alert(data['message']);
                        }
                    },
                    'fail': function (error) {
                        alert("网络出错了...");
                    }
                });

                return false;
                // layer.msg(JSON.stringify(data.field));
                // return false;
            })
        })
    </script>
</body>
</html>
  1. 实现对应的表单form来接收前端的数据
  • forms.py
from wtforms import StringField
from wtforms.validators import Regexp, EqualTo, InputRequired

class SignupForm(Form):
    username = StringField(validators=[InputRequired(message="请输入您的用户名!")])
    password1 = StringField(validators=[Regexp(r"[0-9a-zA-Z_\.]{6,20}", message="请输入正确格式的密码!")])
    password2 = StringField(validators=[EqualTo("password1", message="两次输入的密码不一致!")])

    def get_error(self):
        message = self.errors.popitem()[1][0]
        return message
  1. 实现对应的模型model
  • models.py
from exts import db
import shortuuid
from werkzeug.security import generate_password_hash, check_password_hash
from datetime import datetime


class FrontUser(db.Model):
    __tablename__ = "frontuser"
    id = db.Column(db.String(100), primary_key=True, default=shortuuid.uuid)
    username = db.Column(db.String(100), nullable=False)
    _password = db.Column(db.String(100), nullable=False)
    join_time = db.Column(db.DateTime, default=datetime.now)

    def __init__(self, *args, **kwargs):
        if "password" in kwargs:
            self.password = kwargs.get("password")
            kwargs.pop("password")
        super(FrontUser, self).__init__(*args, **kwargs)

    @property
    def password(self):
        return self._password

    @password.setter
    def password(self, newpwd):
        self._password = generate_password_hash(newpwd)

    def check_password(self, rawpwd):
        return check_password_hash(self._password, rawpwd)
  1. 实现对应的视图
  • views.py
from flask import Blueprint, render_template, views, request, session, g, jsonify
from apps.front.forms import SignupForm, SigninForm
from apps.front.models import EnterpriseNode
from .decorators import login_required
from exts import db
import config


bp = Blueprint("front", __name__)

@bp.route('/')
@login_required
def index():
    return render_template('front/front_index.html')

class SignupView(views.MethodView):
    def get(self):
        return render_template('front/front_signup.html')

    def post(self):
        form = SignupForm(request.form)
        if form.validate():
            username = form.username.data
            password = form.password1.data
            user = FrontUser(
                username=username,
                password=password,
            )
            db.session.add(user)
            db.session.commit()
            return jsonify({'code': 200, 'message': '注册成功', 'data': None)
        else:
            print(form.get_error())
            return jsonify({'code': 400, 'message'=form.get_error(), 'data': None)


bp.add_url_rule('/signup/', view_func=SignupView.as_view('signup'))

到这里,用户的注册功能就实现了

登录功能

这里只给出views.py的代码,其他的可以参考注册功能的实现

class SigninView(views.MethodView):
    def get(self):
        return render_template('front/front_signin.html')

    def post(self):
        form = SigninForm(request.form)
        if form.validate():
            username = form.enterprise.data
            password = form.password.data
            remember = form.remember.data

            user = FrontUser.query.filter_by(username=username).first()

            # 验证是否存在用户和密码
            if user and user.check_password(password):
                session[config.FRONT_USER_ID] = user.id # 保存session信息,维持登录
                g.front_user = user # 保存到g参数,用于实现登录校验,亦可再html模板中使用
                if remember:
                    session.permanent = True
                return jsonify({'code': 200, 'message': '登录成功', 'data': None})
            else:
                return jsonify({'code': 400, 'message': "用户名或密码错误!", 'data': None})
        else:
            return jsonify({'code': 400, 'message': form.get_error(), 'data': None})
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,547评论 6 477
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,399评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,428评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,599评论 1 274
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,612评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,577评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,941评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,603评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,852评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,605评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,693评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,375评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,955评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,936评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,172评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 43,970评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,414评论 2 342