【问题标题】:Idle Timeout of Authentication Tokens身份验证令牌的空闲超时
【发布时间】:2016-03-16 17:11:10
【问题描述】:

我有一个使用 Flask 和 SQLAlchemy 的应用程序,它允许用户使用我在登录应用程序时提供给他们的身份验证令牌来调用 RESTful API。该应用程序的要求之一是,如果用户空闲 15 分钟,他们的令牌应该过期,他们将被要求再次登录应用程序以获取新令牌。

目前我通过将用户的会话存储在表中来做到这一点

class Session(Model):
    id = Column(Integer, primary_key=True)
    user_id = Column(Integer, ForeignKey('user.id'))
    user = relationship('User')
    creation_time = Column(DateTime, default=datetime.now)
    last_active = Column(DateTime, default=datetime.now)

    _serializer = Serializer('foobar')

    def is_active(self):
        return (datetime.now() - self.last_active) < timedelta(15*60)

    def create_token(self, token):
        return self._serializer.dumps({
            'session_id': self.id, 
            'user_id': self.user_id,
        })

    @staticmethod
    def from_token(self, token):
        try:
            data = self._serializer.loads(token)
        except BadSignature:
            return None

        session = Session.query.get(data.get('session_id'))
        if session and session.is_active() and session.user_id == data.get('user_id'):
            # Refresh the session
            session.last_active = datetime.now()
            return session

class User(Model):
    id = Column(Integer, primary_key=True)
    username = Column(String)

    def generate_token(self):
        return Session(self.id).create_token()

    def verify_auth_token(token):
        session = Session.from_token(token)
        if session:
            return session.user

有没有更优雅、更有效的方法来做到这一点?我见过很多例子,其中令牌存储创建时间并且令牌仅在特定时间段内有效(这允许简单的测试而不必知道它是什么用户等),但我没有能够根据用户的空闲时间找到有关到期的任何信息。

【问题讨论】:

    标签: python flask access-token


    【解决方案1】:

    除了因为session.last_active = … 没有调用session.flush()(这里是SQLAlchemy 会话)而设置的潜在错误之外,这是一种完全合理的方法。

    通常,网站使用非关系型数据存储(例如 Redis)进行会话管理,因为它可以提高性能(RDBMS 的写入速度往往较慢,尤其是在这种情况下,您将执行大量写入操作)。

    不过,逻辑还是一样的:

    def session_from_token(token):
        key = "session:%s" %(token, )
        session_json = redis.get(key)
        if not session_json:
            return None
        session = json.loads(session_json)
        if not session.get("is_valid"):
            return None
        redis.expire(key, 15 * 60)
        return session
    

    【讨论】:

    • 感谢您对此的反馈。因此,如果我正确理解这一点,在现有密钥上调用 redis.expire() 基本上会“刷新”该令牌,以便再使用 15 分钟?
    • 是的,没错。现在,这段代码中还有一个竞争条件(如果密钥在被读取和更新过期之间过期),所以你可能想要更复杂一点的东西……但这是正确的一般想法。
    猜你喜欢
    • 1970-01-01
    • 2016-06-04
    • 2015-08-30
    • 2021-02-01
    • 1970-01-01
    • 1970-01-01
    • 2017-06-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多