主要用到的包:
- Flask-Login: 管理已登录用户的用户会话。
- Werkzeug: 计算密码散列值并进行核对(把密码变成散列值
- itsdangerous: 生成并核对加密安全令牌(确认邮件)。
Werkzeug实现密码散列
若想保证数据库中用户密码的安全,要存储密码的散列值。计算密码散列值的函数接收密码作为输入,使用一种或多种加密算法转换密码,最终得到一个和原始密码没有关系的字符序列。核对密码时,密码散列值可代替原始密码,因为计算散列值的函数是可复现的:只要输入一样,结果就一样。
Werkzeug 中的 security 模块能很方便地实现密码散列值的计算。只需要两个函数,分别用在注册和验证用户阶段。
- generate_password_hash(password, method=pbkdf2:sha1, salt_length=8):这个函数将原始 密码作为输入,以字符串形式输出密码的散列值, 输出的值可保存在用户数据库中。method 和 salt_length 的默认值就能满足大多数需求。
- check_password_hash(hash, password):这个函数的参数是从数据库中取回的密码散列值和用 户输入的密码。返回值为 True 表明密码正确。
在 User 模型中加入密码散列:
from werkzeug.security import generate_password_hash, check_password_hash
class User(db.Model):
password_hash = db.Column(db.String(128))
@property #私有
def password(self):
raise AttributeError('password is not a readable attribute')
@password.setter #只可写不可读
def password(self, password):
self.password_hash = generate_password_hash(password)
def verify_password(self, password):
return check_password_hash(self.password_hash, password)
计算密码散列值的函数通过名为 password 的只写属性实现。设定这个属性的值时,赋值方法会调用Werkzeug 提供的generate_password_hash()函数,并把得到的结果赋值给password_hash 字段。如果试图读取 password 属性的值,则会返回错误,原因很明显,因为生成散列值后就无法还原成原 来的密码了。
verify_password 方法会把传入的密码和存储在 User 模型中的密码散列值进行比对。如果这个方法返回 True,就表明密码是正确的。
创建认证蓝本
把创建程序的过程移入工厂函数后,可以使用蓝本在全局作用域中定义路由。 对于不同的程序功能,使用不同的蓝本。
auth 蓝本的包构造文件创建蓝本对象,再从 views.py 模块中引入 路由。
【app/auth/init.py: 创建蓝本】
from flask import Blueprint
auth = Blueprint('auth',__name__)
from . import views
app/auth/views.py 模块引入蓝本,然后使用route 修饰器定义与认证的 /login/路由。渲染同名占位模板。
【app/templates/auth/login.html】
{% extends "base.html" %}
{% block title %}Flasky - Login{% endblock %}
{% block page_content %}
<div class="page-header">
<h1>Login</h1>
</div>
{% endblock %}
【app/auth/views.py: 蓝本中的路由和视图函数】
from flask import render_template
from . import auth
@auth.route('/login/')
def login():
return render_template('auth/login.html')
注意:
为 render_template() 指定的模板文件保存在 auth 文件夹中。这个文件夹必须在 app/templates 中创建, Flask 认为模板的路径是相对于程序模板文件夹而言的。为了避免与 main 蓝本和后续添加的蓝本发生模板命名冲突,可以把蓝本使用的模板保存在单独的文件夹中。
auth 蓝本要在 app/init.py的create_app() 工厂函数中附加到程序上
【app/init.py: 附加蓝本】
def create_app(config_name):
from .auth import auth
app.register_blueprint(auth,url_prefix='/auth')
return app
url_prefix 是可选参数,使用后,注册后蓝本中定义的所有路由都会加上指定的前缀。例如:/login路由会注册成/auth/login, 在开发 Web 服务器中,完整的 URL 就变成了