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

时间:2014-06-25 01:43:41

标签: python flask subdomain

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

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

通常用户通过domain.com登录

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

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

我担心这里的安全。

2 个答案:

答案 0 :(得分:4)

修改: 之后,在阅读完整的问题后,我发现原来的答案不是您正在寻找的。

我已将此答案底部的原件留给了Google员工,但修改后的版本如下。

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

要确认,我将继续(假设您已经告诉我)您的设置如下:

  • 服务器1:
    • domain.com的Flask应用
  • 服务器2:
    • 用户档案的烧瓶应用,位于username.domain.com

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

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

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

<强>数据库

  • 数据库随时可用
  • 您可能已经实施了数据库
  • 开发人员通常拥有所选数据库的预先知识

记忆(Redis / Memchachd / etc。)

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

您可以在烧瓶herehere中找到一些示例数据库会话。

虽然根据每个用户的经验水平设置Redis会更加困难,但这是我推荐的选项。您可以看到执行此操作的示例here

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

单个Flask应用的旧解决方案

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

SERVER_NAME = "cakeninja.net"

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

这里需要注意的是,如果您只是在本地主机上工作,那将是非常困难的(如果不是不可能的话)。如上所述,浏览器通常不会在名称(TLD)中没有点的情况下将cookie发送到域的子域。在许多操作系统中,默认情况下,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!"

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

class User(db.Model):

    username = db.Column(db.String)


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

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

如果您有任何问题或要求,请询问。这是在我喝咖啡休息期间这样做的,所以有点匆忙。

答案 1 :(得分:1)

您可以使用内置的Flask会话(基于cookie的客户端会话)来执行此操作。要允许用户登录“ .domain.com”中的多个子域,只需指定

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

,并且客户端的浏览器将具有一个会话cookie,该会话cookie允许他登录到“ domain.com”上的每个Flask实例。 仅当Flask的每个实例都具有相同的app.secret_key

时,此方法才有效

有关更多信息,请参见 Same Flask login session across two applications