32

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)

ここで、ユーザーが名前とパスワードをフォームに入力し、データベースと照合して、ユーザーにログインさせたいとします。データベースは問題なく動作し、私にとっては問題ありません。

この「コールバック」関数は、ユーザー ID # を渡され、User オブジェクト (データベースからロードしている内容) を返します。しかし、とにかくユーザーIDはすべて同じ場所から取得されるため、チェック/実行することになっていることを実際には理解できません。私はコールバックを機能させることができますが、それは厄介/ハックのようで、ブラウザが要求するすべてのリソースでデータベースにヒットします。ページを更新するたびに favicon.ico をダウンロードするためにデータベースをチェックしたくありませんが、フラスコログインはこれを強制しているようです。

データベースをもう一度チェックしないと、この関数から User オブジェクトを返す方法がありません。ユーザー オブジェクト/クラスは、ログイン用のフラスコ ルートで作成されるため、コールバックの範囲外になります。

私が理解できないのは、毎回データベースにアクセスすることなく、このコールバック関数に 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

ユーザー オブジェクトを 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

私が完全に理解していない「コールバック」(データベースからプルした後に作成されるユーザーオブジェクトを返す必要があります):

@login_manager.user_loader
def load_user(id):
     return check_db(id)

ログインルート:

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

私のコード「ちょっと」は機能し、ログインとログアウトはできますが、言ったように、他の場所とは異なる名前空間/スコープでユーザーオブジェクトをコールバック関数に提供する必要があるため、絶対にすべてのデータベースにヒットする必要がありますログイン アクションが実行されます。私はそれをすべて間違っていると確信していますが、その方法がわかりません。

Flask-login によって提供されるサンプル コードはこのように実行しますが、これはデータベースのような実際のシナリオではなく、ハードコードされたグローバル ディクショナリからユーザー オブジェクトをプルするためにのみ機能します。DB をチェックして、ユーザーがログイン資格情報を入力したに作成されるユーザー オブジェクト。そして、flask-login でデータベースを使用することを示す他のサンプル コードを見つけることができないようです。

ここで何が欠けていますか?

4

3 に答える 3

31

リクエストごとに DB からユーザー オブジェクトをロードする必要があります。その要件の最も強い理由は、Flask-Login が認証トークンを毎回チェックして、その継続的な有効性を確認することです。このトークンの計算には、ユーザー オブジェクトに格納されているパラメーターが必要になる場合があります。

たとえば、ユーザーが 2 つの同時セッションを持っているとします。そのうちの 1 つで、ユーザーはパスワードを変更します。後続のリクエストでは、アプリケーションを安全にするために、ユーザーは 2 番目のセッションからログアウトし、強制的に再度ログインする必要があります。ユーザーがコンピュータからログアウトするのを忘れたために 2 番目のセッションが盗まれた場合を考えてみてください。状況をすぐに修正するために、パスワードを変更する必要があります。管理者にユーザーを追い出す権限を与えることもできます。

このような強制ログアウトが発生するためには、Cookie に保存されている認証トークンが、1) 新しいパスワードが設定されるたびに変更されるパスワードまたはその他の何かに部分的に基づいている必要があります。2) ビューを実行する前に、DB に保存されているユーザー オブジェクトの最新の既知の属性に対してチェックされます。

于 2012-05-22T05:38:03.387 に答える