Flask:在 domain.com 和 username.domain.com 之间共享会话

I L*_*hon 6 python subdomain flask

我有一个在 domain.com 上运行的烧瓶

我在 username.domain.com 上运行的另一台服务器上还有另一个烧瓶实例

通常用户通过 domain.com 登录

但是,对于付费用户,他们应该登录 username.domain.com

使用flask,如何确保在 domain.com 和 username.domain.com 之间共享会话,同时确保它们只能访问专门匹配的 username.domain.com ?

我很担心这里的安全。

F B*_*aut 6

编辑:后来,在阅读了你的完整问题后,我注意到原来的答案不是你想要的。

我已将原始版本保留在 Google 员工的此答案底部,但修订后的版本在下方。

Cookie 会自动发送到域中的子域(在大多数现代浏览器中,域名必须包含句点(表示 TLD)才能发生此行为)。身份验证需要作为预处理器进行,您的会话需要从集中源进行管理。让我们来看看吧。

为了确认,我将继续假设(根据您告诉我的内容)您的设置如下:

  • 服务器 1:
    • 用于 domain.com 的 Flask 应用程序
  • 服务器 2:
    • 用于在 username.domain.com 上的用户配置文件的 Flask 应用程序

首先必须克服的问题是将会话存储在两个服务器都可以访问的位置。由于默认会话存储在磁盘上(并且两台服务器显然不共享同一个硬盘驱动器),我们需要对现有设置和用于用户配置文件的新 Flask 应用程序进行一些修改。

第一步是选择存储会话的位置,由 DBMS(例如 MySQL、Postgres 等)提供支持的数据库是常见选择,但人们也经常选择将它们放在更短暂的地方,例如 Memcachd 或 Redis。

在这两个截然不同的系统之间进行选择的简短版本分解为以下内容:

数据库

  • 数据库随时可用
  • 您可能已经实现了一个数据库
  • 开发人员通常对他们选择的数据库有一个预先存在的知识

内存(Redis/Memchachd/等)

  • 快得多
  • 系统通常提供基本的数据自我管理
  • 不会对现有数据库产生额外负载

您可以在此处此处在 Flask 中找到一些示例数据库会话。

虽然根据每个用户的经验水平,Redis 会更难设置,但我推荐它。您可以在此处查看执行此操作的示例

我认为其余部分已包含在原始答案中,其中一部分演示了用户名与数据库记录的匹配(较大的代码块)。

单个 Flask 应用程序的旧解决方案

首先,你必须设置 Flask 来处理子域,这就像在你的配置文件中指定一个新的变量名一样简单。例如,如果您的域是 example.com,您可以将以下内容附加到您的 Flask 配置中。

SERVER_NAME = "example.com"
Run Code Online (Sandbox Code Playgroud)

您可以在此处阅读有关此选项的更多信息。

这里需要注意的一点是,如果您只是在本地主机上工作,那么测试这将是非常困难的(如果不是不可能的话)。如上所述,浏览器通常不会费心将 cookie 发送到名称中没有点的域的子域(TLD)。在许多操作系统中,Localhost 也没有设置为默认允许子域。有很多方法可以做到这一点,例如定义您可以查看的自己的 DNS 条目(/etc/hosts在 *UNIX 上,%system32%/etc/hosts在 Windows 上)。

准备好配置后,您需要Blueprint为子域通配符定义一个。

这很容易完成:

from flask import Blueprint
from flask.ext.login import current_user

# Create our Blueprint
deep_blue = Blueprint("subdomain_routes", __name__, subdomain="<username>")

# Define our route
@deep_blue.route('/')
def user_index(username):

    if not current_user.is_authenticated():
        # The user needs to log in
        return "Please log in"
    elif username != current_user.username:
        # This is not the correct user.
        return "Unauthorized"

    # It's the right user!
    return "Welcome back!"
Run Code Online (Sandbox Code Playgroud)

这里的技巧是确保__repr__您的用户对象包含用户名键。例如...

class User(db.Model):

    username = db.Column(db.String)


    def __repr__(self):
       return "<User {self.id}, username={self.username}>".format(self=self)
Run Code Online (Sandbox Code Playgroud)

但需要注意的是,当用户名包含在 URL 中不起作用的特殊字符(空格、@、? 等)时会出现问题。为此,您需要对用户名实施限制,或者首先正确转义名称并在验证时取消转义。

如果您有任何问题或要求,请提出。在我喝咖啡休息时这样做,所以有点匆忙。