【问题标题】:Session on Flask server with Python requests package not working带有 Python 请求包的 Flask 服务器上的会话不起作用
【发布时间】:2016-07-25 19:24:29
【问题描述】:

我已经用 flask_login 编写了 Flask 服务器。要登录用户,我使用login_user(user),并且我的站点的一页使用@login_required 保护。作为客户端,我使用 Python requests 包,代码如下:

import requests
sess = requests.session()
sess.post("http://page.on/my/site", data={"login" : "login", "password" : "password"})

通过此身份验证一切正常,但随后我尝试访问受保护的页面:

r = sess.get("http://secure.page/on/the/site")

这个请求会收到Unauthorized。 这是身份验证后设置的 cookie(现在我的服务器在 localhost 上):

<RequestsCookieJar[<Cookie session=.eJwdzjkSwjAMAMC_uE4h2dbhfCZjHR5oE1Ix_B2Gbst9l2OdeT3K_jrv3MrxjLKX5tqBGbq7MfmPiYi8aA7RAG8haZJoDZ0HrlSG6Oa1-yCqsaJNJlSVAQToNXsAt8UiFQBIbIa3bmYiarYyiEaNsUJjmlLZyn3l-c_Uzxf_2C6a.Cnf0yA.xIUSIFgcvjqwszrbwCA_D2Rqa5k for localhost.local/>]>

顺便说一句,我也用这个:

login_manager.session_protection = "strong"

如何解决这个身份验证问题?

更新:

这是服务器登录代码:

@api.route("/login/", methods=["POST"])
def handle_login_request():
    login, password = str(request.form['login']).lower(), str(request.form['password'])
    # Get and check user here
    login_user(user)
    # update user and get user_data
    return jsonify(user_data)

安全路线:

@api.route("/users_of_group/")
@login_required
def get_user_of_users_group():
    # code here never executes because of @login_required

api 是烧瓶蓝图的名称

UPD2:

这是sess.get返回的页面内容:

 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
 <title>401 Unauthorized</title>
 <h1>Unauthorized</h1>
 <p>The server could not verify that you are authorized to access the URL requested.  You either supplied the wrong credentials (e.g. a bad password), or your browser doesn't understand how to supply the credentials required.</p>

UPD3: 我试着用这个:

r = sess.get("http://secure.page/on/the/site", auth=("login", "password"))

在我可以看到的服务器上,首先用户成功登录,但随后服务器仍然抛出 401。

import requests
sess = requests.session()
login = sess.post("http://page.on/my/site", data={"login" : "login", "password" : "password"})
r = sess.get("http://secure.page/on/the/site", cookies=login.cookies)

同样登录用户,然后抛出 401。

UPD4:

问题似乎出在 login_user 函数中,它不会将 is_authenticated 更改为 True。我使用 SQLAlchemy,这是我的用户类:

class User(db.Model):
    user_id = db.Column(db.Integer, primary_key=True)
    # Many required for my app fields
    is_active = db.Column(db.Boolean, default=False)
    is_authenticated = db.Column(db.Boolean, default=False)
    is_anonymous = db.Column(db.Boolean, default=False)
    is_online = db.Column(db.Boolean, default=False)

    # Init function 

    def get_id(self):
        return str(self.user_id)

还有user_loader:

@login_manager.user_loader
def load_user(user_id):
    print(user_id, type(user_id))
    user = User.query.filter_by(user_id=int(user_id)).first()
    print(user.login, user.is_authenticated)
    return user

Id 打印正确,其类型为str,查询工作正常,但我仍然收到Unauthenticated 错误,最后print user.is_authenticated 为False。

UPD5:

实际上,在login_user 之后打印user.is_authenticated 也会显示False,即使我在login_user 之后调用了session.commit()

【问题讨论】:

  • 根据文档,[当会话保护处于活动状态时,每个请求都会为用户的计算机生成一个标识符(基本上是 IP 地址和用户代理的安全哈希) . 所以看起来这根本不是问题。你能发一个完整的minimal reproducible example吗?
  • @WayneWerner,查看我的更新答案
  • 您的问题中缺少用户加载器和用户类,但我的回答很可能解决了您的问题。

标签: python authentication flask flask-sqlalchemy flask-login


【解决方案1】:

这似乎表现出您解释的行为:

from flask import Flask, request
from flask_login import LoginManager, UserMixin, login_user, login_required, \
                        current_user

app = Flask(__name__)
app.secret_key = 'This is totally a secret'
login_manager = LoginManager()
login_manager.init_app(app)
login_manager.session_protection = 'strong'


class User(UserMixin):
    id = 42

    @property
    def is_authenticated(self):
        print('{!r}'.format(self.id))
        return self.id == 42


@login_manager.user_loader
def get_user(id):
    user = User()
    user.id = id
    return user


@app.route('/login', methods=['GET', 'POST'])
def login():
    if request.method == 'POST':
        user = User()
        user.id = request.form.get('id', 42)
        login_user(user)
    else:
        user = current_user
    return 'Okay, logged in with id {}'.format(user.id)


@app.route('/')
@login_required
def main():
    return 'Hey cool - your id {}'.format(current_user.id)



if __name__ == '__main__':
    app.run(host='0.0.0.0', port=5001, debug=True)

这里要注意的是ID是'42',而不是42。显然,从会话中解码 id 还不够聪明,无法知道您的 ID 不是字符串。通过将我的功能更改为return str(self.id) == '42',它可以工作。我怀疑您的用户加载器或用户类中有类似的设置。

正如我所怀疑的 - 您的用户模型生成了不正确的 is_authenticated。当然,它很可能完全按照您的指示行事。您只需修复经过身份验证的位。请注意,如果您从用户加载器返回None,它将使用匿名用户 - 您可以在您的is_authenticated 方法中硬编码True,除非您尝试跨会话注销用户。

【讨论】:

  • Flask-Login 要求您的用户 ID 为文本类型(unicode 在 2.7 中,str 在 3.x 中)。请参阅get_id 文档。好吧,我想严格来说,你可以覆盖get_id 来进行转换,但get_id 必须返回一个文本类型。
  • 知道有用 - 我怀疑 UserMixin 在幕后为 get_id 做这件事。
  • @WayneWerner,我刚刚尝试过并添加了一些调试打印,但问题完全不同 - 请参阅我的更新答案
  • 其实就是这个问题 - 你的 is_authenticated 值返回 False。我会在我的答案中添加一些细节
  • 我已经稍微更新了我的答案 - 如果你想要一些特定的东西,你将不得不提供其余的登录方法。
猜你喜欢
  • 1970-01-01
  • 2019-01-10
  • 1970-01-01
  • 2016-08-20
  • 1970-01-01
  • 1970-01-01
  • 2014-04-30
  • 1970-01-01
  • 2021-11-18
相关资源
最近更新 更多