如何在Flask-Login中实现user_loader回调

Edm*_*ett 28 python authentication login python-2.x flask

我正在尝试使用Flask和Flask-Login扩展来在Flask应用程序中实现用户身份验证.目标是从数据库中提取用户帐户信息,然后登录用户,但我遇到了困难; 但是,我已将其缩小到Flask-Login行为的特定部分.

根据Flask-Login文档,我需要创建一个user_loader"回调"函数.这个功能的实际目的和实现让我困惑了几天:

您需要提供user_loader回调.此回调用于从存储在会话中的用户ID重新加载用户对象.它应该采用用户的Unicode ID,并返回相应的用户对象.例如:

@login_manager.user_loader
def load_user(userid):
    return User.get(userid)
Run Code Online (Sandbox Code Playgroud)

现在,假设我希望用户在表单中输入名称和密码,检查数据库,然后登录用户.数据库的东西工作正常,对我来说没问题.

这个'回调'函数想要传递一个用户ID#,并返回User对象(我从数据库加载的内容).但是我并没有真正得到它应该检查/做的事情,因为无论如何用户ID都是从同一个地方拉出来的.我可以"排序"让回调起作用,但它似乎凌乱/黑客,并且它会使用浏览器请求的每个资源来访问数据库.我真的不想检查我的数据库,以便在每次刷新页面时下载favicon.ico,但烧瓶登录似乎正在强制执行此操作.

如果我不再次检查数据库,那么我无法从此函数返回User对象.User对象/类在flask路径中创建以便登录,因此超出了回调的范围.

我无法弄清楚的是如何将User对象传递给此回调函数,而不必每次都访问数据库.或者,否则要弄清楚如何以更有效的方式做到这一点.我必须遗漏一些基本的东西,但我已经盯着它看了几天,抛出了各种各样的功能和方法,没有任何结果.

以下是我的测试代码中的相关摘录.用户类:

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

     def is_active(self):
          return self.active
Run Code Online (Sandbox Code Playgroud)

我用来将用户对象返回到Flask-Login的user_loader回调函数的函数:

def check_db(userid):

     # query database (again), just so we can pass an object to the callback
     db_check = users_collection.find_one({ 'userid' : userid })
     UserObject = UserClass(db_check['username'], userid, active=True)
     if userObject.id == userid:
          return UserObject
     else:
          return None
Run Code Online (Sandbox Code Playgroud)

'回调',我不完全理解(必须返回User对象,它是从数据库中提取后创建的):

@login_manager.user_loader
def load_user(id):
     return check_db(id)
Run Code Online (Sandbox Code Playgroud)

登录路线:

@app.route("/login", methods=["GET", "POST"])
def login():
     if request.method == "POST" and "username" in request.form:
          username = request.form["username"]

          # check MongoDB for the existence of the entered username
          db_result = users_collection.find_one({ 'username' : username })

          result_id = int(db_result['userid'])

          # create User object/instance
          User = UserClass(db_result['username'], result_id, active=True)

          # if username entered matches database, log user in
          if username == db_result['username']:
               # log user in, 
               login_user(User)
               return url_for("index"))
          else:
               flash("Invalid username.")
      else:
           flash(u"Invalid login.")
      return render_template("login.html")
Run Code Online (Sandbox Code Playgroud)

我的代码'有点'有效,我可以登录和退出,但正如我所说,它必须完全打到数据库的所有内容,因为我必须在不同的命名空间/范围内提供一个User对象到其他的命名空间/范围登录操作发生.我很确定我做错了,但我无法弄明白.

flask-login提供的示例代码以这种方式实现,但这只能起作用,因为它从全局硬编码字典中提取User对象,而不是像数据库那样必须检查数据库的真实场景.用户输入登录凭据创建的用户对象.而且我似乎找不到任何其他示例代码来说明使用带有flask-login的数据库.

这里缺少什么?

jd.*_*jd. 22

每次请求时都需要从DB加载用户对象.该要求的最强原因是Flask-Login将每次检查身份验证令牌以确保其持续有效性.此令牌的计算可能需要存储在用户对象上的参数.

例如,假设用户有两个并发会话.在其中一个中,用户更改其密码.在后续请求中,用户必须退出第二个会话并强制重新登录才能使您的应用程序安全.想想第二个会话被盗的情况,因为您的用户忘记退出计算机 - 您希望更改密码以立即修复此情况.您可能还希望让管理员能够将用户踢出局.

对于这种强制注销,存储在cookie中的身份验证令牌必须1)部分基于密码或每次设置新密码时更改的其他内容; 2)在运行任何视图之前,检查用户对象的最新已知属性 - 这些属性存储在DB中.