Flask账户注册(2)

Flask账户注册(2)

Flask账户注册

综合使用所学习过的知识,搭建登录页面

代码示例:https://github.com/ningwenyan/demo_code/tree/master/flask_demo_code/T26

总览

❯ tree
.
├── app.py
├── auth
│   ├── auth_form.py
│   ├── __init__.py
│   └── views.py
├── common
│   ├── errors.py
│   ├── exts.py
│   ├── __init__.py
│   ├── mailModel.py
│   └── sqlModel.py
├── config.py
├── manager.py
├── migrations
│   ├── alembic.ini
│   ├── env.py
│   ├── README
│   ├── script.py.mako
│   └── versions
│       ├── 44bc21866b0d_.py
│       └── __pycache__
│           └── 44bc21866b0d_.cpython-37.pyc
├── static
└── templates
    ├── 401.html
    ├── 403.html
    ├── 404.html
    ├── 500.html
    └── auth
        ├── change_email.html
        ├── change_password.html
        ├── index.html
        ├── login.html
        ├── mail
        │   ├── change_email.html
        │   ├── change_email.txt
        │   ├── confirm.html
        │   ├── confirm.txt
        │   ├── password_reset.html
        │   └── password_reset.txt
        ├── main
        │   └── reset_password.html
        ├── personal.html
        ├── register.html
        └── reset_password.html

设计数据库(加密密码)

简单的来说,这里只设计一个用户的一些基本属性,需要注意的是,用户的密码需要在数据库中加密,避免用户密码泄露.

# config.py

# 设置数据库的基本信息
# 数据库连接
DB_URI = 'mysql+pymysql://root:2008.Cn123@192.168.0.101:3306/flask_login_demo1' # 确保数据库存在
# 指定数据库连接
SQLALCHEMY_DATABASE_URI = DB_URI
SQLALCHEMY_TRACK_MODIFICATIONS = False
# common/exts.py

from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# app.py

from common.exts import db
db.init_app(app)
# common/sqlModel.py
from flask_login import UserMixin
from .exts import db, flask_bcrypt


class User(db.Model, UserMixin):
   __tablename__ = 'users'

   id = db.Column(db.Integer, primary_key=True)
   email = db.Column(db.String(64), unique=True, index=True)
   username = db.Column(db.String(64), unique=True, index=True)
   _password_hash = db.Column(db.String(128))   # 隐藏属性值,不能被外部访问
   confirmed = db.Column(db.Boolean, default=False)  # flag, 标识是否注册激活

   # 密码加密
   # 使用_password作为类属性,这是私有属性,不允许访问
   # 映射一个password方法
   def __init__(self, email, username, password, **kwargs):
       self.email = email
       self.username = username
       self.password = password

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

   @password.setter
   def password(self, raw_password):
       # 加密密码
       self._password_hash = flask_bcrypt.generate_password_hash(raw_password)

   @password.deleter
   def password(self):
       del self._password_hash

   # 定义解密密码
   def check_password(self, password):
       # 如果原始密码和加密后的密码相同,返回True
       return flask_bcrypt.check_password_hash(self._password_hash, password)

表单自验证用户

设计完数据库映射后,需要考虑一个问题,就是注册的重复性问题.一个邮箱只能注册一次,一个用户名有且只能存在一个.

# auth/auth_form.py

from flask_wtf import FlaskForm
from wtforms import StringField, PasswordField, BooleanField, ValidationError
from wtforms.validators import Length, EqualTo, Regexp, Required, DataRequired, Email
from common.sqlModel import User

class LoginForm(FlaskForm):
   email = StringField(validators=[Email(), DataRequired(), Length(164)])
   password = PasswordField(validators=[Length(124)])
   confirmed = BooleanField()

class RegisterForm(FlaskForm):
   email = StringField('Email', validators=[Email(), Length(164), DataRequired()])
   username = StringField('Username', validators=[
       DataRequired(), Length(164),
       Regexp('^[A-Za-z][A-Za-z0-9_.]*$'0,
              'Username must have only letters, numbers, dots or '
              'underscores')
   ])
   password = PasswordField('Password', validators=[Length(124), Required()])
   con_password = PasswordField('Confirm Password', validators=[EqualTo('password')])

   # 自定义Field,验证邮箱和用户
   def validate_email(self, field):
       if User.query.filter_by(email = field.data.lower()).first():
           raise ValidationError('Email already registered.')

   def validate_username(self, filed):
       if User.query.filter_by(username = filed.data).first():
           raise  ValidationError('Username already in use.')

定义视图函数

定义注册和登录网站的视图函数

#!/usr/bin/env python
# coding=utf-8

from . import auth_bp
from flask import render_template, redirect, url_for, request, flash
from .auth_form import LoginForm, RegisterForm
from common.exts import db
from common.sqlModel import User
from flask_login import login_user, login_required, current_user

@auth_bp.route('/')
def index():
   return render_template('auth/index.html')

# 设计注册,登录页面

@auth_bp.route('/register/', methods = ['GET', 'POST'])
def register():
   form = RegisterForm()
   if form.validate_on_submit():
       """注册账户"""
       email = form.email.data.lower()
       username = form.username.data
       password = form.password.data
       user = User(email, username, password)
       db.session.add(user)
       db.session.commit()
       return redirect(url_for('auth.login'))
   return render_template('auth/register.html')

@auth_bp.route('/login/', methods = ['GET', 'POST'])
def login():
   form = LoginForm()
   if form.validate_on_submit():
       """登录用户"""
       email = form.email.data.lower()
       password = form.password.data
       user = User.query.filter_by(email=email).first()
       # 数据库中有账户,并且能够验证密码
       if user is not None and user.check_password(password):
           login_user(user)
           # 判断 next
           next = request.args.get('next')
           if next is None or not next.startswitch('/'):
               next = redirect(url_for('auth.index'))
           return redirect(url_for(next or 'auth.personal'))
       else:
           flash('无效的邮箱或密码.')
   return render_template('auth/login.html')

# 限制登录用户访问 login_required
@auth_bp.route('/personal/')
@login_required
def personal():
   return render_template('auth/personal.html', user = current_user)

@auth_bp.route('/logout/')
@login_required
def logout():
   return redirect(url_for('auth.index'))

使用flash返回消息后,需要在前端添加反馈

<!----login.html------>
   <form action="" method="post">
        {# 解决csrf_token #}
       <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
       <div>
           <label for="">Email:</label>
           <input type="text" placeholder="Email" name="email">
       </div>
       <div>
           <label for="">Password:</label>
           <input type="password" placeholder="Password" name="password">
       </div>
       <div>
           <label for="">Remember me:</label>
           <input type="checkbox" name="remember">
       </div>
       <div>
           <input type="submit" name="submit" value="Login">
       </div>
       {#  接受后端返回的flash消息  #}
       {% with messages = get_flashed_messages() %}
         {% if messages %}
           {% for message in messages %}
             {{ message }}
           {% endfor %}
         {% endif %}
       {% endwith %}

   </form>

定义Flask Login回调函数

# sqlModel.py

@login_manager.user_loader
def load_user(user_id):
   return User.query.get(int(user_id))

定义CSRF保护

我们使用了Flask Form表单验证,所以这里要进行CSRF保护

# exts.py

from flask_sqlalchemy import SQLAlchemy
from flask_login import LoginManager
from flask_bcrypt import Bcrypt
from flask_wtf.csrf import CSRFProtect

db = SQLAlchemy()
login_manager = LoginManager()
flask_bcrypt = Bcrypt()
csrf = CSRFProtect()
# app.py
from common.exts import db, login_manager, flask_bcrypt, csrf

csrf.init_app(app)
# config.py
import os

# CSRF SECRET_KEY
SECRET_KEY = os.urandom(12)

对所有使用POST表单的页面添加保护

       {# 解决csrf_token #}
        <input type="hidden" name="csrf_token" value="{{ csrf_token() }}">

指定Flask Login登录路由

# app.py
# 指定登录的URL
login_manager.login_view = 'auth.login'

– END –


原文始发于微信公众号(Flask学习笔记):Flask账户注册(2)

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/36408.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!