Restful 身份验证:允许从多台计算机登录吗?

发布于 2024-08-13 16:56:54 字数 206 浏览 2 评论 0原文

我们的 Rails 应用程序使用 Restful 身份验证进行用户/会话管理,似乎从多台计算机登录同一帐户会终止其他计算机上的会话,从而终止“记住我”功能。

假设我在家并登录该应用程序(并选中“记住我”)。然后我去办公室登录(并选中“记住我”)。然后,当我回到家时,我返回应用程序并必须重新登录。

如何允许从多台计算机登录并保持“记住我”功能在所有计算机上正常工作?

Our Rails app is using Restful Authentication for user/session management and it seems that logging in to the same account from multiple computers kills the session on the other computers, thus killing the "Remember me" feature.

So say I'm at home and log in to the app (and check "Remember me"). Then I go to the office and log in (and also check "Remember me"). Then, when I return home, I return to the app and and have to re-log in.

How can I allow logging in from multiple machines and keep the "Remember me" functionality working across them all?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

等数载,海棠开 2024-08-20 16:56:54

这样做会牺牲一些安全性,但这绝对是可能的。您应该可以通过两种方法来实现此目的。

首先,您可以重写用户模型中的 make_token 方法。该模型目前的实现如下。

def make_token
  secure_digest(Time.now, (1..10).map{ rand.to_s })
end

每次用户登录时,无论是否带有 cookie,都会调用 make_token 方法,为用户生成并保存新的 remember_token。如果您有一些其他用户独有且无法猜测的值,则可以替换 make_token 方法。

def make_token
  secure_digest(self.some_secret_constant_value)
end

这将确保令牌永远不会改变,但它也将使任何获得令牌的人都可以冒充用户。

除此之外,如果您查看 authenticated_system.rb 文件,您应该能够更改此方法以适合您。

def handle_remember_cookie!(new_cookie_flag)
  return unless @current_<%= file_name %>
  case
  when valid_remember_cookie? then @current_<%= file_name %>.refresh_token # keeping same expiry date
  when new_cookie_flag        then @current_<%= file_name %>.remember_me 
  else                             @current_<%= file_name %>.forget_me
  end
  send_remember_cookie!
end

您会注意到,此方法调用用户模型中的三个方法:refresh_tokenremember_meforget_me

  def remember_me
    remember_me_for 2.weeks
  end

  def remember_me_for(time)
    remember_me_until time.from_now.utc
  end

  def remember_me_until(time)
    self.remember_token_expires_at = time
    self.remember_token            = self.class.make_token
    save(false)
  end

  # 
  # Deletes the server-side record of the authentication token.  The
  # client-side (browser cookie) and server-side (this remember_token) must
  # always be deleted together.
  #
  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end

  # refresh token (keeping same expires_at) if it exists
  def refresh_token
    if remember_token?
      self.remember_token = self.class.make_token 
      save(false)      
    end
  end

所有这三种方法都会重置令牌。 forget_me 将其设置为 nil,而其他两个将其设置为 make_token 返回的值。您可以在用户模型中重写这些方法,以防止它们重置令牌(如果令牌存在且未过期)。这可能是最好的方法,或者您可以向 handle_remember_cookie! 方法添加一些额外的逻辑,尽管这可能需要更多工作。

如果我是你,我会重写用户模型中的 remember_me_untilforget_merefresh_token。以下应该有效。

def remember_me_until(time)
  if remember_token?
    # a token already exists and isn't expired, so don't bother resetting it
    true
  else
    self.remember_token_expires_at = time
    self.remember_token            = self.class.make_token
    save(false)
  end
end

# 
# Deletes the server-side record of the authentication token.  The
# client-side (browser cookie) and server-side (this remember_token) must
# always be deleted together.
#
def forget_me
  # another computer may be using the token, so don't throw it out
  true
end

# refresh token (keeping same expires_at) if it exists
def refresh_token
  if remember_token?
    # don't change the token, so there is nothing to save
    true     
  end
end

请注意,通过这样做,您将删除保护您免遭令牌窃取的功能。但这是您可以做出的成本效益决定。

You are going to sacrifice some security by doing this, but it's definitely possible. There are two ways you should be able to accomplish this.

In the first, you can override the make_token method in your user model. The model is currently implemented as follows.

def make_token
  secure_digest(Time.now, (1..10).map{ rand.to_s })
end

Every time a user logs in, with or without a cookie, the make_token method is called which generates and saves a new remember_token for the user. If you had some other value that was unique to the user that couldn't be guessed, you could replace the make_token method.

def make_token
  secure_digest(self.some_secret_constant_value)
end

This would ensure that the token never changes, but it would also enable anyone that got the token to impersonate the user.

Other than this, if you take a look at the handle_remember_cookie! method in the authenticated_system.rb file, you should be able to change this method to work for you.

def handle_remember_cookie!(new_cookie_flag)
  return unless @current_<%= file_name %>
  case
  when valid_remember_cookie? then @current_<%= file_name %>.refresh_token # keeping same expiry date
  when new_cookie_flag        then @current_<%= file_name %>.remember_me 
  else                             @current_<%= file_name %>.forget_me
  end
  send_remember_cookie!
end

You'll notice that this method calls three methods in the user model, refresh_token, remember_me, and forget_me.

  def remember_me
    remember_me_for 2.weeks
  end

  def remember_me_for(time)
    remember_me_until time.from_now.utc
  end

  def remember_me_until(time)
    self.remember_token_expires_at = time
    self.remember_token            = self.class.make_token
    save(false)
  end

  # 
  # Deletes the server-side record of the authentication token.  The
  # client-side (browser cookie) and server-side (this remember_token) must
  # always be deleted together.
  #
  def forget_me
    self.remember_token_expires_at = nil
    self.remember_token            = nil
    save(false)
  end

  # refresh token (keeping same expires_at) if it exists
  def refresh_token
    if remember_token?
      self.remember_token = self.class.make_token 
      save(false)      
    end
  end

All three of these methods reset the token. forget_me sets it to nil, whereas the other two set it to the value returned by make_token. You can override these methods in the user model, to prevent them from resetting the token if it exists and isn't expired. That is probably the best approach, or you could add some additional logic to the handle_remember_cookie! method, though that would likely be more work.

If I were you, I would override remember_me_until, forget_me, and refresh_token in the user model. The following should work.

def remember_me_until(time)
  if remember_token?
    # a token already exists and isn't expired, so don't bother resetting it
    true
  else
    self.remember_token_expires_at = time
    self.remember_token            = self.class.make_token
    save(false)
  end
end

# 
# Deletes the server-side record of the authentication token.  The
# client-side (browser cookie) and server-side (this remember_token) must
# always be deleted together.
#
def forget_me
  # another computer may be using the token, so don't throw it out
  true
end

# refresh token (keeping same expires_at) if it exists
def refresh_token
  if remember_token?
    # don't change the token, so there is nothing to save
    true     
  end
end

Note that by doing this, you're taking out the features that protect you from token stealing. But that's a cost benefit decision you can make.

喜爱纠缠 2024-08-20 16:56:54

您可以更改 remember_token 的内容来实现此目的。您可以将其设置为:

self.remember_token = encrypt("#{email}--extrajunkcharsforencryption")

而不是

self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")

现在,令牌没有任何特定于计算机或时间的信息,您可以从多台计算机保持登录状态。

You can change what the remember_token is to achieve this. You can set it to:

self.remember_token = encrypt("#{email}--extrajunkcharsforencryption")

instead of

self.remember_token = encrypt("#{email}--#{remember_token_expires_at}")

Now there is nothing computer or time specific about the token and you can stay logged in from multiple machines.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文