79

我试图了解它是如何Flask-Login工作的。

我在他们的文档中看到他们使用预先填充的用户列表。我想玩一个数据库存储的用户列表。

但是,我不了解此Flask-Login模块中的某些内容。

@login_manager.user_loader
def load_user(userid):
    #print 'this is executed',userid
    return user(userid, 'asdf')

每次请求都会调用此代码?这用于加载我的用户对象的所有详细信息?

现在,我有这个代码:

@app.route('/make-login')
def make_login():
    username = 'asdf'
    password = 'asdf'
    user_data = authenticate(username, password)
    user_obj = user(user_data[0], user_data[1])
    login_user(user_obj)
    return render_template('make-login.html')

当我访问 /make-login 时,我想登录。

我的用户类:

class user(object):
    def __init__(self, id, username, active=True):
        self.username = username
        self.id = id
        #self.active = active
    def is_authenticated(self):
        return True  

    def is_active(self):
        return True

    def is_anonymous(self):
        return False

    def get_id(self):
        return 5

另外,我写了另外两个函数来验证/注册

def authenticate(username, password):

    cursor = db.cursor()
    password = md5.md5(password).hexdigest()
    try:
        query = "SELECT * FROM `users` WHERE `username` = %s AND `password` = %s"
        cursor.execute(query, (username, password))
        results = cursor.fetchall()
        #print results[0][0]
        #print "here i am"
        if not results:
            return False
        else:
            user_data = [results[0][0], results[0][1]]
            return user_data
            #self.authenticated = True
            #self.user_id = results[0][0]
            #session['username']  = results['username']
            #print type(results)
    except db.Error, e:
        return 'There was a mysql error'    

def register(username, password, email, *args):
    cursor = db.cursor()
    password = md5.md5(password).hexdigest()
    try:
        #query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES ('%s', '%s', '%s')" % (username, password, email)
        query = "INSERT INTO `users` (`username`, `password`, `email`) VALUES (%s, %s, %s)"
        cursor.execute(query, (username, password, email))
        db.commit()
        return True
    except db.Error, e:
        print 'An error has been passed. %s' %e
        db.rollback()
        return False

我不知道如何使它Flask-Login与 MySQL 一起工作。另外,我不知道用户是否已登录。如何获取用户 ID 或用户名?

任何人都可以在某些行中向我解释这是如何Flask-Login工作的?

4

6 回答 6

80

Flask-login 实际上没有用户后端,它只是处理会话机制以帮助您登录和注销用户。你必须告诉它(通过装饰方法),什么代表一个用户,你还需要弄清楚如何知道用户是否“活跃”(因为“活跃”在不同的应用程序中可能意味着不同的东西)。

您应该阅读文档并确定它做什么和不做什么。在这里,我只专注于将它与 db 后端连接起来。

首先,定义一个用户对象;它代表您的用户的属性。然后,该对象可以查询数据库、LDAP 或其他任何东西,它是将登录机制与数据库后端连接起来的钩子。

为此,我将使用登录示例脚本。

class User(UserMixin):
    def __init__(self, name, id, active=True):
        self.name = name
        self.id = id
        self.active = active

    def is_active(self):
        # Here you should write whatever the code is
        # that checks the database if your user is active
        return self.active

    def is_anonymous(self):
        return False

    def is_authenticated(self):
        return True

一旦你创建了用户对象,你需要编写一个加载用户的方法(基本上,User从上面创建一个类的实例)。使用用户 ID 调用此方法。

@login_manager.user_loader
def load_user(id):
     # 1. Fetch against the database a user by `id` 
     # 2. Create a new object of `User` class and return it.
     u = DBUsers.query.get(id)
    return User(u.name,u.id,u.active)

完成这些步骤后,您的登录方法将执行以下操作:

  1. 检查用户名和密码是否匹配(针对您的数据库) - 您需要自己编写此代码。

  2. 如果身份验证成功,您应该将用户实例传递给login_user()

于 2012-08-22T21:38:17.337 回答
16

Flask-login 将在每次请求之前尝试加载用户。所以是的,您下面的示例代码将在每个请求之前被调用。它用于检查当前会话中的用户 ID,并将加载该 ID 的用户对象。

@login_manager.user_loader
def load_user(userid):
    #print 'this is executed',userid
    return user(userid, 'asdf')        

如果您查看 github 上的 Flask-login 源代码,函数 init_app 下有一行:

app.before_request(self._load_user)

因此,在每次请求之前,都会调用 _load_user 函数。_load_user 函数实际上根据条件调用另一个函数“reload_user()”。最后, reload_user() 函数调用您编写的回调函数(示例中的 load_user() )。

此外,flask-login 仅提供登录/注销用户的机制。它不关心您是否使用 mysql 数据库。

于 2012-08-22T22:28:36.560 回答
7

根据Flask-Login的文档,必须返回用户对象,如果未找到用户 ID,则应返回 None 而不是 Exception。

@login_manager.user_loader
def load_user(userid):
    try:
        #: Flask Peewee used here to return the user object
        return User.get(User.id==userid)
    except User.DoesNotExist:
        return None
于 2014-02-23T15:29:59.637 回答
6

您可能希望使用Flask-Security,它将 Flask-Login 与 SQLAlchemy 结合起来进行数据库访问,并自动执行大部分用户记录的后端处理。

快速入门教程将帮助您入门。将 app.config['SQLALCHEMY_DATABASE_URI'] 设置为 MySQL 数据库连接字符串。

于 2014-05-02T12:45:56.410 回答
1

在解释完所有内容后,我将尝试使用代码给出一个简单的使用示例,同时在下面回答:

完成这些步骤后,您的登录方法将执行以下操作:

  1. 检查用户名和密码是否匹配(针对您的数据库) - 您需要自己编写此代码。
  2. 如果身份验证成功,您应该将用户实例传递给 login_user()

假设这是项目的结构:

├─stackoverflow
  │   run.py
  │
  └───site
      │   forms.py
      │   models.py
      │   routes.py
      │   site.db
      │   __init__.py
      │
      ├───static
      │       main.css
      │
      └───templates
              about.html
              account.html
              home.html
              layout.html
              login.html
              register.html

我们最感兴趣的是模型:

# models.py
from site import db, login_manager
from flask_login import UserMixin


@login_manager.user_loader
def load_user(user_id):
    return User.query.get(int(user_id))


class User(db.Model, UserMixin):
    id = db.Column(db.Integer, primary_key=True)
    username = db.Column(db.String(20), unique=True, nullable=False)
    email = db.Column(db.String(120), unique=True, nullable=False)
    image_file = db.Column(db.String(20), nullable=False, default='default.jpg')
    password = db.Column(db.String(60), nullable=False)

    def __repr__(self):
        return f"User('{self.username}', '{self.email}', '{self.image_file}')"

我们将在用户登录时调用它,更准确地说是在用户注册之后——在用户存在之后。

具体来说,需要实现的两个步骤的答案可以在以下两行代码中找到:

  1. 检查用户名和密码是否匹配(针对您的数据库) - 您需要自己编写此代码。

A:if user and bcrypt.check_password_hash(user.password, form.password.data):

  1. 如果身份验证成功,您应该将用户实例传递给 login_user()

A:login_user(user, remember=form.remember.data)

# routes.py

from flask import render_template, url_for, flash, redirect, request
from site import app, db, bcrypt
from site.forms import RegistrationForm, LoginForm
from site.models import User
from flask_login import login_user, current_user, logout_user, login_required



@app.route("/")
@app.route("/home")
def home():
    return render_template('home.html', title="Welcome")


@app.route("/register", methods=['GET', 'POST'])
def register():
    if current_user.is_authenticated:
        return redirect(url_for('home'))
    form = RegistrationForm()
    if form.validate_on_submit():
        hashed_password = bcrypt.generate_password_hash(form.password.data).decode('utf-8')
        user = User(username=form.username.data, email=form.email.data, password=hashed_password)
        db.session.add(user)
        db.session.commit()
        flash('Your account has been created! You are now able to log in', 'success')
        return redirect(url_for('login'))
    return render_template('register.html', title='Register', form=form)


@app.route("/login", methods=['GET', 'POST'])
def login():
    if current_user.is_authenticated:
        return redirect(url_for('home'))
    form = LoginForm()
    if form.validate_on_submit():
        user = User.query.filter_by(email=form.email.data).first()
        if user and bcrypt.check_password_hash(user.password, form.password.data):
            login_user(user, remember=form.remember.data)
            next_page = request.args.get('next')
            return redirect(next_page) if next_page else redirect(url_for('home'))
        else:
            flash('Login Unsuccessful. Please check email and password', 'danger')
    return render_template('login.html', title='Login', form=form)


# Views that require your users to be logged in can be decorated with the `login_required` decorator

@app.route("/account")
@login_required
def account():
    return render_template('account.html', title='Account')


# When the user is ready to log out:

@app.route("/logout")
@login_required
def logout():
    logout_user()
    return redirect(url_for('home'))

然后,您可以使用代理访问已登录的用户,该current_user代理在每个模板中都可用:

{% if current_user.is_authenticated %}
  Hi {{ current_user.username }}!
{% endif %}

默认情况下,当用户尝试login_required在未登录的情况下访问视图时,Flask-Login 将闪烁一条消息并将他们重定向到登录视图。(如果未设置登录视图,它将以 401 错误中止。)

登录视图的名称可以设置为LoginManager.login_view

login_manager.login_view = 'login'

# __init__.py

from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from flask_bcrypt import Bcrypt
from flask_login import LoginManager

app = Flask(__name__)
app.config['SECRET_KEY'] = 'ENTER_SECRET_KEY'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///site.db'
db = SQLAlchemy(app)
bcrypt = Bcrypt(app)
login_manager = LoginManager(app)
login_manager.login_view = 'login'
login_manager.login_message_category = 'info'

from site import routes

最后,运行项目:

# run.py

from site import app

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

我希望,除了一个很好的解释之外,这个简单的例子是有帮助的。

于 2021-09-28T18:27:13.320 回答
0

这是一个使用登录的 Flask 示例:https : //bitbucket.org/leafstorm/flask-login/src/3160dbfc7cfc/example/login-example.py 您需要为每个需要登录的方法使用@login_required。例如,

@app.route('/make-login')
@login_required
def make_login():
    ...
于 2012-08-22T17:25:25.130 回答