menu Chancel's blog
rss_feed
Chancel's blog
有善始者实繁,能克终者盖寡。

flask的用户登录设计

作者:Chancel Yang, 创建:2021-01-31, 字数:5278, 已阅:855, 最后更新:2024-03-10

开发一个博客程序,对于登录这一块的要求与一般的程序不太一样,只有一个用户登录需求

Flask本身提供了非常简洁的登录模块 flask-login模块能帮助我们快速生成登录程序模块

利用这个模块我们可以快速迭代一个登录模块,再搭配Google Authenticator的二次验证来提高登录安全强度

以下开发环境测试均基于Python3.6.9

1. 登录

1.1. Flask应用

编译app.py,写一个最简单的flask应用

Python
from flask import Flask

app = Flask(__name__)

@app.route('/'):
    return 'hello world'

1.2. flask-login

安装 flask-login 扩展

Bash
pip3 install flask-login

app.py中初始化flask-login登录模块

Python
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 类,这样我们的类就具备这些属性方法的默认实现了。

Python
class User(UserMixin):
    pass

接着声明一个用户加载方法,用于用户登录后访问flask应用时,告诉Flask应用当前用户是谁

如果返回为空则说明用户不存在或者登录状态无效

Python
@login_manager.user_loader
def load_user(user_id):
    # User应为登录用户对象的集合
    return User.get(user_id)

为了验证登录效果,添加一个需要登录用户才能访问的路由

Python
# 用户如果没有登录会被导向这个蓝图进行登录
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 '认证失败'

到目前为止,代码如下所示

Python
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的标准实现如下

Python
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

Bash
pip install pyotp

将刚才的程序改造如下

Python
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-loginpyotp可以简单地实现一个高安全性的登录逻辑

如果是博客类型的程序,只需在配置文件中存入一个用于pyotp的Secert字符串就可以轻松地实现这个登录逻辑

参考资料


[[replyMessage== null?"发表评论":"发表评论 @ " + replyMessage.m_author]]

account_circle
email
web_asset
textsms

评论列表([[messageResponse.total]])

还没有可以显示的留言...
gravatar
[[messageItem.m_author]] [[messageItem.m_author]]
[[messageItem.create_time]]
[[getEnviron(messageItem.m_environ)]]
[[subMessage.m_author]] [[subMessage.m_author]] @ [[subMessage.parent_message.m_author]] [[subMessage.parent_message.m_author]]
[[subMessage.create_time]]
[[getEnviron(messageItem.m_environ)]]