简体   繁体   English

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

[英]Simple flask_login example - is this the correct way?

Below I tried to create a very reductive and simple flask_login implementation.下面我尝试创建一个非常简化且简单的flask_login实现。 While it works, I just want to make sure I'm doing it the correct way.虽然它有效,但我只想确保我以正确的方式进行操作。 In particular, is my password authentication method correct?特别是,我的密码验证方法是否正确? By correct I mean, should I be using another flask_login function to do this check for me?正确的意思是,我应该使用另一个 flask_login function 来为我做这个检查吗? Do I have any unnncessary code here?我这里有任何不必要的代码吗?

Still new to python and flask, any advice/edits would be appreciated.对于 python 和 flask 还是新手,任何建议/编辑将不胜感激。

Username/password check:用户名/密码检查:

 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>
        '''

Entire flask_login attempt:整个 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)






Everything works.一切正常。 However, there are a few principles you should consider when you are handling user data:但是,在处理用户数据时应该考虑一些原则:

  • Passwords should never be stored in the database as was given by the user密码不应该像用户给出的那样存储在数据库中
  • For code re-usability, consider separation of concerns对于代码可重用性,考虑关注点分离

Werkzeug is normally used to do password hashing. Werkzeug通常用于进行密码散列。 When a password has been “hashed” it means it has been turned into a scrambled representation of itself.当密码被“散列”后,这意味着它已经变成了自己的加密表示。

This is how it works in a python shell:这是它在 python shell 中的工作方式:

>>> from werkzeug.security import generate_password_hash
>>> hash = generate_password_hash('my_password')
>>> hash
'pbkdf2:sha256:150000$aRIbsDyl$90ae44b1a5c679e08685c75ff0750df7c6670582a5839072d35a713316816760'
>>>

my_password has been transformed into a long encoded string through a series of cryptographic operations that have no known reverse operation, which means that a person that obtains the hashed password will be unable to use it to obtain the original password. my_password已经通过一系列没有已知反向操作的密码操作变成了一个长编码字符串,这意味着获得哈希密码的人将无法使用它来获得原始密码。

To verify a user's password, you can do:要验证用户的密码,您可以执行以下操作:

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

This password hash is what you should store in your database:此密码 hash 是您应该存储在数据库中的密码:

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)

You can now create the login logic in your routes:您现在可以在路由中创建登录逻辑:

@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)

The validation checks basically try to find out if the user already exists in the database.验证检查基本上试图找出用户是否已经存在于数据库中。 If they do, and their username and password is correct, then they are logged in. Otherwise, they are redirected to try logging in again.如果他们这样做了,并且他们的用户名和密码是正确的,那么他们就会登录。否则,他们会被重定向以再次尝试登录。

As far as separation of concerns goes, what you want to do is you ensure that your application is built using modules.关注点分离而言,您要做的是确保您的应用程序是使用模块构建的。 I mean, there is a module that handles database issues, another that handles views, another errors et cetera.我的意思是,有一个模块处理数据库问题,另一个处理视图,另一个错误等等。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM