开发一个博客程序,对于登录这一块的要求与一般的程序不太一样,只有一个用户登录需求
Flask本身提供了非常简洁的登录模块 flask-login
模块能帮助我们快速生成登录程序模块
利用这个模块我们可以快速迭代一个登录模块,再搭配Google Authenticator的二次验证来提高登录安全强度
以下开发环境测试均基于Python3.6.9
1. 登录
1.1. Flask应用
编译app.py
,写一个最简单的flask应用
from flask import Flask
app = Flask(__name__)
@app.route('/'):
return 'hello world'
1.2. flask-login
安装 flask-login
扩展
pip3 install flask-login
在app.py
中初始化flask-login登录模块
from flask import Flask
from flask_login import LoginManager
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
@app.route('/'):
return 'hello world'
然后根据flask-login的文档,设计一个用户类,这个类必须实现以下3种属性1种方法
- is_authenticated
- is_active
- is_anonymous
- get_id()
这几个属性以及方法用于告诉Flask应用当前用户的状态,我们也可以选择直接继承 UserMixin
类,这样我们的类就具备这些属性方法的默认实现了
class User(UserMixin):
pass
接着声明一个用户加载方法,用于用户登录后访问flask应用时,告诉Flask应用当前用户是谁
如果返回为空则说明用户不存在或者登录状态无效
@login_manager.user_loader
def load_user(user_id):
# User应为登录用户对象的集合
return User.get(user_id)
为了验证登录效果,添加一个需要登录用户才能访问的路由
# 用户如果没有登录会被导向这个蓝图进行登录
login_manager.login_view = "manages_blueprint.login"
@manages_blueprint.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
# 请求方法为GET返回登录界面的HTML文档
return render_template('login.html', title='登录')
if request.method == 'POST':
# 请求方法为POST则做登录校验
username = request.json.get('username', None)
password = request.json.get('password', None)
# 判断用户密码是否正确,正确则调用 login_user()方法将用户信息添加到会话中
if username and password:
user = User()
login_user(user, remember=False)
return flask.redirect(next or flask.url_for('index'))
return '认证失败'
到目前为止,代码如下所示
from flask_login import LoginManager
from flask import Flask
class User(UserMixin):
pass
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "manages_blueprint.login"
@login_manager.user_loader
def load_user(user_id):
# User应为登录用户对象的集合
return User.get(user_id)
@app.route('/'):
return 'hello world'
@manages_blueprint.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
# 请求方法为GET返回登录界面的HTML文档
return render_template('login.html', title='登录')
if request.method == 'POST':
# 请求方法为POST则做登录校验
username = request.json.get('username', None)
password = request.json.get('password', None)
# 判断用户密码是否正确,正确则调用 login_user()方法将用户信息添加到会话中
if username and password:
user = User()
login_user(user, remember=False)
return flask.redirect(next or flask.url_for('index'))
return '认证失败'
如上,Flask的登录设计简洁,做为博客的登录设计非常合适
在校验用户信息完成后,只需要调用 login_user
方法便可以实现所有登录相关的信息
2. 二步验证
二步验证(Two-Factor Authentication,简称2FA
)是一种通过使用两个独立的验证因素来增强账户安全性的身份验证方法,传统的身份验证方法通常只使用用户名和密码,而二步验证添加了第二个因素,通常是手机应用生成的临时验证码
2.1. pyotp
PyOTP
是一个Python库,用于生成和验证一次性密码(One-Time Passwords,简称OTP),OTP是一种用于身份验证的临时密码,通常在每次使用时都会生成一个新的密码
使用Python来完成一个简单的二步验证只需要引入一个 pyotp
的库即可,一个简单的例子如下
pyotp的标准实现如下
import pyotp
SECRET_KEY = pyotp.random_base32()
totp = pyotp.totp.TOTP(SECRET_KEY,interval=60)
print('当前验证码->%s' % totp.now())
qrcode_text = totp.provisioning_uri(name='chancel', issuer_name='Chancel\'s blog')
引入pyotp库之后创建一个KEY,使用这个KEY我们就可以生成一个totp对象
调用这个对象的provisioning_uri我们就可以生成一段二维码的文本,使用任意文本转二维码工具后获得一个二维码
F2A客户端可以考虑微软以及Google的客户端,都有着非常优秀的体验
安装地址可参考文末链接
使用Google出品的扫描并绑定这个二维码,则可以看到一个动态的6位数,这个动态码将与 totp.now()
输出保持一致
2.2. 二步验证
继续第一步中的例子,先安装pyotp
pip install pyotp
将刚才的程序改造如下
from flask_login import LoginManager
from flask import Flask
import pyotp
class User(UserMixin):
pass
app = Flask(__name__)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "manages_blueprint.login"
# 新增totp对象
totp = pyotp.totp.TOTP("[your_secert_key]", interval=60)
@login_manager.user_loader
def load_user(user_id):
return User.get(user_id)
@app.route('/'):
return 'hello world'
@manages_blueprint.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'GET':
return render_template('login.html', title='登录')
if request.method == 'POST':
username = request.json.get('username', None)
password = request.json.get('password', None)
code = request.json.get('code', None)
# 新增totp.verify(code)方法判定二次验证码是否正确
if username and password and totp.verify(code):
user = User()
login_user(user, remember=False)
return flask.redirect(next or flask.url_for('index'))
return '认证失败'
3. 尾声
使用flask-login
和pyotp
可以简单地实现一个高安全性的登录逻辑
如果是博客类型的程序,只需在配置文件中存入一个用于pyotp
的Secert字符串就可以轻松地实现这个登录逻辑
参考资料