Flask-login 用户登录后设置为匿名

Seb*_*ian 5 python flask flask-login

我对烧瓶和烧瓶登录很陌生,我已经为此苦苦挣扎了好几天。

我尝试像这样登录用户:

from creds import auth_username, auth_password, pgsql_dbuser, pgsql_dbpassword, pgsql_db1name
from flask import Flask, render_template, request, Response, redirect, url_for
from flask.ext.bcrypt import Bcrypt
from flask.ext.login import LoginManager, login_required, login_user, current_user, logout_user
import logging
import psycopg2
import uuid
import datetime

app = Flask(__name__)
app.secret_key = str(uuid.uuid4()) # <- required by login_manager.init_app(app)

bcrypt = Bcrypt(app)
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.login_view = 'index'


@app.route('/', methods=['GET','POST'])
def index():
        page_name = '/'
        if request.method == 'POST':
                email = request.form['email']
                candidate_password = request.form['password']
                user = finduserindbbyemail(email)
                if user != None:
                        password_hash = checkuserpasswordindb(email)
                        if bcrypt.check_password_hash(password_hash, candidate_password):
                                user_object = User(user)
                                result = login_user(user_object) # <- here for successful login
                                return redirect(url_for('loggedin', user_object=type(user_object), user=user, result=result, current_user=current_user))
                        else:
                                user_object = User(user)
                                error_message = "The password you entered is incorrect"
                                return render_template('index.html', error_message=error_message)
                else:
                        error_message = "The email address you entered does not match any we have in our records"
                        return render_template('index.html', error_message=error_message)

        if request.method == 'GET':
                return render_template('index.html')
Run Code Online (Sandbox Code Playgroud)

我有一个 User 类和一个用户回调:

class User():

        def __init__(self, user):
                self.user = user

        def is_authenticated(self):
                return True

        def is_active(self):
                return True

        def is_anonymous(self):
                return False

        def get_id(self):
                return unicode(self.user)

@login_manager.user_loader
def load_user(user):
        con = psycopg2.connect(database=pgsql_db1name, user=pgsql_dbuser, password=pgsql_dbpassword, host='localhost')
        uuid = "'"+user+"'"
        cur = con.cursor()
        cur.execute("SELECT uuid FROM users WHERE uuid = "+ uuid)
        uuid = cur.fetchone()
        con.close()
        if uuid != None:
                user = unicode(uuid[0])
                return User.get_id(user)
        else:
                return None
Run Code Online (Sandbox Code Playgroud)

身份验证成功后(显然?),用户将被重定向到具有装饰器的登录页面@login_required。但应用程序没有加载登录页面,而是将用户重定向到登录页面,告诉我用户尚未登录?如果尝试将值发送到页面并删除@login_required装饰器以便我可以看到该页面,这就是我在“登录”后在浏览器中看到的内容:

current_user.is_authenticated() = False
current_user.is_active() = False
current_user.is_anonymous() = True
current_user.get_id() = None
user_object = <type 'instance'>
user = 2ca1296c-374d-43b4-bb7b-94b8c8fe7e44
login_user = True
current_user = <flask_login.AnonymousUserMixin object at 0x7f2aec80f190> Logout
Run Code Online (Sandbox Code Playgroud)

我的用户似乎尚未登录并被视为匿名?谁能看到我做错了什么吗?我很难理解这是如何运作的。

Seb*_*ian 2

所以..我设法让它工作,但没有使用 user_loader 回调。无论出于何种原因,我的用户加载器表现出与此相同的行为: 使用静态用户的 Flask-login 总是产生 401-未经授权

不管怎样,我根据这个例子使用了 request_loader 回调: http://gouthamanbalaraman.com/blog/minimal-flask-login-example.html

因此,对于登录的用户,从这里开始:

if bcrypt.check_password_hash(password_hash, candidate_password):
    user_object = User(user, password_hash)
    result = login_user(user_object) # <- here for successful login
    token = user_object.get_auth_token(user, password_hash) 
    return redirect(url_for('loggedin', token=token))
Run Code Online (Sandbox Code Playgroud)

我创建了一个用户对象,其中包含用户的 ID 及其密码哈希值。然后我登录用户。然后我使用itsdangerous 创建用户ID 和密码散列的时间序列化令牌。get_auth_token 函数是 User 类的一部分。它看起来像这样:

class User():

    def __init__(self, user, password_hash):
        self.user = user
        self.password = password_hash
        .
        .
        .
    def get_auth_token(self, user, password):
        data = [str(self.user), self.password]
        return serializer.dumps(data, salt=serializer_secret)
Run Code Online (Sandbox Code Playgroud)

您需要在代码开头的某个位置创建一个序列化器:

serializer = URLSafeTimedSerializer(serializer_secret)
Run Code Online (Sandbox Code Playgroud)

因此,创建令牌后,将其作为 URL 查询参数传递到登录视图。当您尝试加载 login_required 页面(例如我的登录页面)(成功登录后 login_user 将我重定向到该页面)时,将执行 request_loader 回调。它看起来像这样:

@login_manager.request_loader
def load_user_from_request(request):
    if request.args.get('token'):
        token = request.args.get('token')
        max_age = 1
        try:
            data = serializer.loads(token, salt=serializer_secret, max_age=max_age)
            username = data[0]
            password_hash = data[1]
            found_user = finduserindbbyuuid(username)
            found_password = checkuserpasswordindbbyuuid(username)
            if found_user and found_password == password_hash:
                user_object = User(found_user, password_hash)
                if (user_object.password == password_hash):
                    return user_object
                else:
                    return None
            else:
                return None

        except BadSignature, e:
            pass
    else:
        return None
Run Code Online (Sandbox Code Playgroud)

这就是我的 user_loader 失败的地方。我登录成功,但 user_loader 始终返回 None,因此我的用户将被视为匿名。

因此,请求加载器会检查请求 URL 的查询字符串中是否包含“token”参数。如果是这样,它会获取它的值并使用它的危险来反序列化数据。您可以使用定时序列化器使令牌过期,但也有非定时序列化器。令牌反序列化后,获取用户和密码哈希值并检查数据库是否存在,这与 user_loader 应该工作的方式完全相同..我想?我的 user_loader 不起作用,所以我很可能做错了。

无论如何,如果用户和密码在数据库中匹配,则返回用户对象和 bam,登录有效。

我不确定我这样做是否正确,因为我几乎是凭感觉行事。我看到人们使用 token_loader 而不是 request_loader 回调函数来加载令牌的示例,但我无法弄清楚如何设置和从客户端获取身份验证令牌。也许我会弄清楚......有一天......

如果你有同样的问题,也许这可能有帮助?或者让我知道你的想法

干杯