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

7 投票
2 回答
4688 浏览
提问于 2025-04-18 10:58

我在 domain.com 上运行了一个 Flask 应用。

我还有另一个 Flask 应用在另一台服务器上,运行在 username.domain.com。

通常情况下,用户是通过 domain.com 登录的。

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

我想知道,使用 Flask 的话,怎么才能确保 domain.com 和 username.domain.com 之间的会话是共享的,同时又能确保他们只能访问对应的 username.domain.com?

我对安全性这块比较担心。

2 个回答

3

你可以使用Flask自带的会话功能,这种会话是基于浏览器的cookie。为了让用户能够在'.domain.com'的多个子域名上登录,你只需要指定

 app.config['SESSION_COOKIE_DOMAIN'] = '.domain.com'

这样,用户的浏览器就会有一个会话cookie,这样他就可以在'domain.com'下的每个Flask实例上登录。这个方法只有在每个Flask实例使用相同的app.secret_key时才有效。

想了解更多信息,可以查看 在两个应用程序之间共享Flask登录会话

6

编辑:后来,在阅读了你的完整问题后,我发现原来的回答并不是你想要的。

我把原来的回答留在了这个答案的底部,方便搜索的朋友们,但下面是修订后的版本。

Cookies会自动发送到同一域名下的子域名(在大多数现代浏览器中,域名必须包含一个点(表示顶级域名)才能实现这种行为)。身份验证需要在处理之前完成,并且你的会话需要从一个集中源进行管理。我们来一步步看看。

为了确认,我将假设(根据你告诉我的)你的设置如下:

  • 服务器 1:
    • 用于 domain.com 的 Flask 应用
  • 服务器 2:
    • 用于 username.domain.com 的用户资料 Flask 应用

首先需要解决的问题是将会话存储在两个服务器都能访问的位置。因为默认情况下,会话存储在磁盘上(而且两个服务器显然不共享同一个硬盘),我们需要对现有设置和新的用户资料 Flask 应用进行一些修改。

第一步是选择存储会话的位置,常见的选择是使用 MySQL、Postgres 等数据库管理系统(DBMS)来存储,但人们也常常选择将其放在更临时的地方,比如 Memcached 或 Redis。

在这两种截然不同的系统之间选择的简短版本如下:

数据库

  • 数据库随时可用
  • 你可能已经实现了一个数据库
  • 开发者通常对他们选择的数据库有预先的了解

内存(Redis/Memcached 等)

  • 速度明显更快
  • 系统通常提供基本的数据自我管理
  • 不会对现有数据库造成额外负担

你可以在 flask 中找到一些数据库会话的例子 这里这里

虽然 Redis 的设置可能会根据每个用户的经验水平而有所不同,但我推荐这个选项。你可以在 这里 查看一个示例。

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

单个 Flask 应用的旧解决方案

首先,你需要设置 Flask 来处理子域名,这只需在配置文件中指定一个新的变量名。例如,如果你的域名是 example.com,你可以在 Flask 配置中添加以下内容。

SERVER_NAME = "example.com"

你可以在 这里 阅读更多关于这个选项的信息。

这里需要快速提到的是,如果你只是使用 localhost,这将非常困难(甚至不可能)进行测试。如上所述,浏览器通常不会向没有点(顶级域名)的域名的子域发送 cookies。许多操作系统默认情况下也不允许 localhost 设置子域。你可以通过定义自己的 DNS 条目来实现这一点(在 *UNIX 上是 /etc/hosts,在 Windows 上是 %system32%/etc/hosts)。

一旦你的配置准备好了,你需要为子域通配符定义一个 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!"

这里的关键是确保你的用户对象的 __repr__ 包含一个用户名键。例如……

class User(db.Model):

    username = db.Column(db.String)


    def __repr__(self):
       return "<User {self.id}, username={self.username}>".format(self=self)

不过需要注意的是,当用户名包含特殊字符(空格、@、? 等)时,会出现问题,这些字符在 URL 中无法使用。为此,你需要对用户名施加限制,或者在验证时先正确转义名称,然后再进行解码。

如果你有任何问题或请求,请随时问我。这个回答是在我喝咖啡的间隙写的,所以有点匆忙。

撰写回答