简单 flask_login 示例 - 这是正确的方法吗?

Simple flask_login example - is this the correct way?

下面我尝试创建一个非常简化和简单的 flask_login 实现。虽然它有效,但我只想确保我以正确的方式进行操作。特别是,我的密码验证方法是否正确?我的意思是正确的,我应该使用另一个 flask_login 函数来为我做这个检查吗?我这里有什么不需要的代码吗?

python 和 flask 还是新手,任何 advice/edits 都将不胜感激。

Username/password 检查:

 if (username, password) in users_db.items():
            login_user(User(username))
            return redirect(request.args.get("next"))
        else:
            return abort(401)
    else:
        return Response('''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=password name=password>
            <p><input type=submit value=Login>
        </form>
        '''

整个 flask_login 尝试:

from flask import Flask, jsonify, render_template, request, url_for, redirect, session, abort, Response
from flask_login import LoginManager, UserMixin, login_required, login_user, logout_user 


from flask_wtf import Form
from wtforms import StringField, PasswordField, SubmitField
from wtforms.validators import DataRequired


#################### Instantiate APP ###################################

'''Application Factory'''
app = Flask(__name__)

app.config['SECRET_KEY'] = 'shhsecret'   #make this more random and secret, i recommend using os.urandom(50) 
#################### Authentication ####################################

# flask-login
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = "login"


class LoginForm(Form):
    username = StringField('Your username', validators=[DataRequired()])
    password = PasswordField('Your password', validators=[DataRequired()])
    submit = SubmitField('Sign In')


# silly user model
class User(UserMixin):

    def __init__(self, username):

        self.id = username
        self.password = users_db[username]
        
    def __repr__(self):
        return "%s/%s" % ( self.id, self.password)
    
    def is_active(self):
        return True

#users database (used dictionary just as an example) 
users_db = { 'bill':'password1'
            ,'jondoe': 'password2'
            ,'elonmusk' : 'passwordtesla'

}

# create users from our users database above     
users_activated = {User(key) for (key,value) in users_db.items()} 



  

# some protected url
@app.route('/protectedurl')
@login_required
def protectedurl_func():
    return Response("Hello World!")

 

# somewhere to login
@app.route("/login", methods=["GET", "POST"])
def login():
    if request.method == 'POST':
        username = request.form['username']
        password = request.form['password']        
        if (username, password) in users_db.items():
            login_user(User(username))
            return redirect(request.args.get("next"))
        else:
            return abort(401)
    else:
        return Response('''
        <form action="" method="post">
            <p><input type=text name=username>
            <p><input type=password name=password>
            <p><input type=submit value=Login>
        </form>
        ''')


# somewhere to logout
@app.route("/logout")
@login_required
def logout():
    logout_user()
    return Response('<p>Logged out</p>')


# handle login failed
@app.errorhandler(401)
def page_not_found(e):
    return Response('<p>Login failed</p>')


# callback to reload the user object        
@login_manager.user_loader
def load_user(userid):
    return User(userid)



if __name__ == '__main__':
    app.run(debug=True, use_reloader=True)






一切正常。但是,在处理用户数据时应考虑一些原则:

  • 密码绝不能像用户给定的那样存储在数据库中
  • 为了代码的可重用性,考虑关注点分离

Werkzeug 通常用于密码散列。当一个密码被“散列”后,这意味着它已经变成了它自己的一个加扰表示。

这是它在 python shell:

中的工作方式
>>> from werkzeug.security import generate_password_hash
>>> hash = generate_password_hash('my_password')
>>> hash
'pbkdf2:sha256:150000$aRIbsDylae44b1a5c679e08685c75ff0750df7c6670582a5839072d35a713316816760'
>>>

my_password经过一系列没有已知逆向操作的密码运算变成了一个长编码字符串,这意味着获得哈希密码的人将无法使用它来获得原密码.

要验证用户密码,您可以这样做:

>>> from werkzeug.security import check_password_hash
>>> check_password_hash(hash, 'my_password')
True
>>> check_password_hash(hash, 'another_password')
False
>>>

此密码哈希是您应该存储在数据库中的内容:

from app import db

class User(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(64), index=True, unique=True)
    password_hash = db.Column(db.String(128))

    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    def check_password(self, password):
        return check_password_hash(self.password_hash, password)

您现在可以在路由中创建登录逻辑:

@app.route('/login', methods=['GET', 'POST'])
def login():
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(username=form.username.data).first()
        if user is None or not user.check_password(form.password.data):
            flash('Invalid username or password')
            return redirect(url_for('login'))
        login_user(user, remember=form.remember_me.data)
        return redirect(url_for('index'))
    return render_template('login.html', title='Sign In', form=form)

验证检查主要是尝试查明用户是否已存在于数据库中。如果他们这样做,并且他们的用户名和密码正确,那么他们将登录。否则,他们将被重定向以尝试再次登录。

关注点分离而言,您要做的是确保您的应用程序是使用模块构建的。我的意思是,有一个模块处理数据库问题,另一个模块处理视图,另一个错误等等。