Restful Authentication: разрешить вход с нескольких компьютеров? - PullRequest
6 голосов
/ 04 декабря 2009

Наше приложение Rails использует Restful Authentication для управления пользователями / сессиями, и кажется, что вход в одну и ту же учетную запись с нескольких компьютеров убивает сеанс на других компьютерах, тем самым убивая функцию «Запомнить меня».

Итак, скажите, что я дома, и войдите в приложение (и отметьте «Запомнить меня»). Затем я иду в офис и захожу (а также отмечаю «Запомнить меня»). Затем, когда я вернусь домой, я вернусь в приложение и снова войду в систему.

Как я могу разрешить вход в систему с нескольких машин и поддерживать функционирование функции «Запомнить меня» на всех них?

Ответы [ 2 ]

9 голосов
/ 08 декабря 2009

Делая это, вы собираетесь пожертвовать некоторой безопасностью, но это определенно возможно. Это можно сделать двумя способами.

Во-первых, вы можете переопределить метод 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

Это позволит гарантировать, что токен никогда не изменится, но также позволит любому, кто получил токен, выдать себя за пользователя.

Кроме этого, если вы посмотрите на метод handle_remember_cookie! в файле 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_token, remember_me и 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

Все три из этих методов сбрасывают токен. forget_me устанавливает значение nil, тогда как другие два устанавливают значение, возвращаемое make_token. Вы можете переопределить эти методы в пользовательской модели, чтобы предотвратить сброс маркера, если он существует и срок его действия не истек. Это, вероятно, лучший подход, или вы могли бы добавить некоторую дополнительную логику в метод handle_remember_cookie!, хотя это, вероятно, потребует больше усилий.

На вашем месте я бы переопределил remember_me_until, forget_me и refresh_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

Обратите внимание, что этим вы убираете функции, защищающие вас от кражи токенов. Но это решение, которое вы можете принять с точки зрения затрат.

0 голосов
/ 04 декабря 2009

Вы можете изменить значение remember_token для достижения этой цели. Вы можете установить его на:

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

вместо

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

Теперь в токене нет ничего конкретного компьютера или времени, и вы можете оставаться в системе с нескольких компьютеров.

...