用 Flask 来写个轻博客 (19) — 以 Bcrypt 密文存储账户信息与实现用户登陆表单

来源:互联网 发布:中经网数据库 编辑:程序博客网 时间:2024/05/22 05:45

目录

  • 目录
  • 前文列表
  • 修改 User Model
  • Flask Bcrypt
  • 将 Bcrypt 应用到 User Model 中
  • 创建登陆表单

前文列表

用 Flask 来写个轻博客 (1) — 创建项目
用 Flask 来写个轻博客 (2) — Hello World!
用 Flask 来写个轻博客 (3) — (M)VC_连接 MySQL 和 SQLAlchemy
用 Flask 来写个轻博客 (4) — (M)VC_创建数据模型和表
用 Flask 来写个轻博客 (5) — (M)VC_SQLAlchemy 的 CRUD 详解
用 Flask 来写个轻博客 (6) — (M)VC_models 的关系(one to many)
用 Flask 来写个轻博客 (7) — (M)VC_models 的关系(many to many)
用 Flask 来写个轻博客 (8) — (M)VC_Alembic 管理数据库结构的升级和降级
用 Flask 来写个轻博客 (9) — M(V)C_Jinja 语法基础快速概览
用 Flask 来写个轻博客 (10) — M(V)C_Jinja 常用过滤器与 Flask 特殊变量及方法
用 Flask 来写个轻博客 (11) — M(V)C_创建视图函数
用 Flask 来写个轻博客 (12) — M(V)C_编写和继承 Jinja 模板
用 Flask 来写个轻博客 (13) — M(V)C_WTForms 服务端表单检验
用 Flask 来写个轻博客 (14) — M(V)C_实现项目首页的模板
用 Flask 来写个轻博客 (15) — M(V)C_实现博文页面评论表单
用 Flask 来写个轻博客 (16) — MV(C)_Flask Blueprint 蓝图
用 Flask 来写个轻博客 (17) — MV(C)_应用蓝图来重构项目
用 Flask 来写个轻博客 (18) — 使用工厂模式来生成应用对象

修改 User Model

使用明文的方式存储账户数据是一个非常严重的安全隐患,要保护用户的密码,就要使用 哈希算法的单向加密方法
哈希算法:对于相同的数据,哈希算法总是会生成相同的结果。
单向加密:就是信息在加密之后,其原始信息是不可能通过密文反向计算出来的。
所以,为了账户信息的安全,在数据库中存储的密码应该是被哈希过的哈希值。但是需要注意,哈希算法的种类很多,其中大多是是不安全的,可以被黑客 暴力破解
暴力破解:通过遍历各种数据的哈希值,来找到匹配的哈希值,从而获取你的密码权限。
所以这里我们使用 Bcrypt 哈希算法,这是一种被刻意设计成抵消且缓慢的哈希计算方式,从而极大的加长了暴力破解的时间和成本,以此来保证安全性。

Flask Bcrypt

  • 安装
(env) [root@flask-dev JmilkFan-s-Blog]# pip install Flask-Bcrypt(env) [root@flask-dev JmilkFan-s-Blog]# pip freeze > requirements.txt

NOTE: Flask Bcrypt 与 Flask SQLAlchemy 一样需要使用 app 对象来进行初始化,我们在 jmilkfansblog 目录下新建一个 extensions.py 来实现我们以后会使用到的所有 Flask 扩展。

将 Bcrypt 应用到 User Model 中

  • extensions.py
from flask.ext.bcrypt import Bcrypt# Create the Flask-Bcrypt's instancebcrypt = Bcrypt()

NOTE 1:以后所有会使用到的 Flask 扩展都会在 extensions.py 中。

  • jmilkfansblog/__init__.py
from flask import Flask, redirect, url_forfrom jmilkfansblog.models import dbfrom jmilkfansblog.controllers import blogfrom jmilkfansblog.extensions import bcryptdef create_app(object_name):    """Create the app instance via `Factory Method`"""    app = Flask(__name__)    # Set the config for app instance    app.config.from_object(object_name)    # Will be load the SQLALCHEMY_DATABASE_URL from config.py to db object    db.init_app(app)    # Init the Flask-Bcrypt via app object    bcrypt.init_app(app)    @app.route('/')    def index():        # Redirect the Request_url '/' to '/blog/'        return redirect(url_for('blog.home'))    # Register the Blueprint into app object    app.register_blueprint(blog.blog_blueprint)    return app
  • NOTE 2:模块中的导入路径最好使用绝对路径。

  • models.py

from jmilkfansblog.extensions import bcryptclass User(db.Model):    """Represents Proected users."""    # Set the name for table    __tablename__ = 'users'    id = db.Column(db.String(45), primary_key=True)    username = db.Column(db.String(255))    password = db.Column(db.String(255))    # one to many: User ==> Post     # Establish contact with Post's ForeignKey: user_id    posts = db.relationship(        'Post',        backref='users',        lazy='dynamic')    def __init__(self, id, username, password):        self.id = id        self.username = username        self.password = self.set_password(password)    def __repr__(self):        """Define the string format for instance of User."""        return "<Model User `{}`>".format(self.username)    def set_password(self, password):        """Convert the password to cryptograph via flask-bcrypt"""        return bcrypt.generate_password_hash(password)    def check_password(self, password):        return bcrypt.check_password_hash(self.password, password)
  • set_password(self, password):在设定密码的时候,将明文密码转换成为 Bcrypt 类型的哈希值。
  • check_password(self, password):检验输入的密码的哈希值,与存储在数据库中的哈希值是否一致。

    • 验证
>>> from uuid import uuid4>>> user = User(id=str(uuid4()), username='test_1', password='fanguiju')>>> db.session.add(user)>>> db.session.commit()>>> >>> >>> user = User.query.filter_by(username='test_1').first()>>> user.passwordu'$2b$12$omKgt8saJydyfbBYwMnms.1ihw7Ox6alBPKdYsPUKtzaBQaNM4Guy'

创建登陆表单

  • forms.py
from flask_wtf import Formfrom wtforms import (    StringField,    TextField,    TextAreaField,    PasswordField,    BooleanField,    ValidationError)from wtforms.validators import DataRequired, Length, EqualTo, URLfrom jmilkfansblog.models import Userclass LoginForm(Form):    """Login Form"""    username = StringField('Username', [DataRequired(), Length(max=255)])    password = PasswordField('Password', [DataRequired()])    def validate(self):        """Validator for check the account information."""        check_validata = super(LoginForm, self).validate()        # If validator no pass        if not check_validata:            return False        # Check the user whether exist.        user = User.query.filter_by(username=self.username.data).first()        if not user:            self.username.errors.append('Invalid username or password.')            return False        # Check the password whether right.        if not user.check_password(self.password.data):            self.username.errors.append('Invalid username or password.')            return False
  • NOTE 1: LoginForm 重载的 validate() 中调用了父类 Form 中的 validate(),用于检验用户输入的数据是否通过了 username/password 字段的检验器。
  • NOTE 2:LoginForm 重载的 validate() 不仅仅实现了父类的功能,还实现了检验 username 是否存在和用户输入的 password 是否正确的功能。子类重载父类的方法结合 super() 内置函数是 Python OOP 中常用的技巧
2 0
原创粉丝点击