Я иногда использую это поведение как альтернативу следующему шаблону:
singleton = None
def use_singleton():
global singleton
if singleton is None:
singleton = _make_singleton()
return singleton.use_me()
Если singleton
используется только use_singleton
, мне нравится следующий шаблон в качестве замены:
# _make_singleton() is called only once when the def is executed
def use_singleton(singleton=_make_singleton()):
return singleton.use_me()
Я использовал это для создания экземпляров клиентских классов, которые обращаются к внешним ресурсам, а также для создания dicts или списков для memoization.
Поскольку я не думаю, что этот шаблон хорошо известен, Я поставил короткий комментарий для защиты от будущих недоразумений.
Вы не можете этого сделать.
Но ничто из этого не гарантирует, что только один пользователь использует этот логин и что эти 105 IP со всего мира принадлежат не только одному уникальному пользователю, который использует прокси-сервер или что-то еще.
И последнее: вам никогда не понадобится это в Интернет.
UPD
Однако я прошу ограничить использование несколькими пользователями одной и той же учетной записи, которая, как я считаю, должна быть возможной
blockquote>Таким образом, вы можете сохранить некоторый токен, который будет содержать некоторые зашифрованные данные: IP + секретная строка + пользовательский агент + версия браузера пользователя + пользовательская ОС + любая другая личная информация:
encrypt(IP + "some secret string" + request.user_agent + ...)
. А затем вы можете установить сеанс или файл cookie с этим токеном. И с каждым запросом вы можете его получить: если пользователь тот же? Использует ли он тот же браузер и ту же версию браузера из той же ОС и т. Д.Также вы можете использовать динамические токены: вы меняете токен на каждый запрос, поэтому только один пользователь может использовать систему за сеанс, поскольку каждый запрос токен будет изменен, другой пользователь выйдет из системы, пока его токен будет истек.
Хотя вы не можете надежно запретить пользователям публиковать учетную запись, то, что вы можете сделать (я думаю), предотвращает одновременную регистрацию одного пользователя в одной учетной записи. Не уверен, что этого достаточно для вашей бизнес-модели, но она затрагивает множество проблем, обсуждаемых в других ответах. Я реализовал что-то, что в настоящее время находится в бета-версии, и, похоже, работает достаточно хорошо - здесь есть некоторые примечания
Я обнаружил, что решение в оригинальной публикации не работает для меня. Я хотел, чтобы первый пользователь вышел из системы и на странице входа. Кроме того, метод sign_out_and_redirect(current_user)
не работает так, как я ожидал. Используя переопределение SessionController в этом решении, я модифицировал его для использования веб-сокетов следующим образом:
def create
super
force_logout
end
private
def force_logout
logout_subscribe_address = "signout_subscribe_response_#{current_user[:id]}"
logout_subscribe_resp = {:message => "#{logout_subscribe_address }: #{current_user[:email]} signed out."}
WebsocketRails[:signout_subscribe].trigger(signout_subscribe_address, signout_subscribe_resp)
end
end
Убедитесь, что все веб-страницы подписываются на канал выписки и связывают его с тем же действием logout_subscribe_address
. В моей заявке каждая страница также имеет кнопку «выйти», которая выдает клиенту через сеансовое действие «Уничтожить». Когда ответ веб-камеры запускается на веб-странице, он просто нажимает на эту кнопку - вызывается логика выписки, а первый пользователь представлен на странице входа.
Это решение также не требует skip_before_filter :check_concurrent_session
и модель login_token
, поскольку она запускает принудительный выход без предубеждений.
Для записи, devise_security_extension
, как представляется, обеспечивает функциональность, чтобы сделать это. Он также устанавливает соответствующее предупреждение, предупреждающее первого пользователя о том, что произошло (я еще не понял, как это сделать).
Что касается фактического внедрения в Devise, добавьте это в свою модель User.rb. Что-то вроде этого автоматически выйдет из системы (непроверено).
def token_valid?
# Use fl00rs method of setting the token
session[:token] == cookies[:token]
end
## Monkey Patch Devise methods ##
def active_for_authentication?
super && token_valid?
end
def inactive_message
token_valid? ? super : "You are sharing your account."
end
Отслеживать IP-адреса uniq, используемые для каждого пользователя. Время от времени выполняйте анализ этих IP-адресов - обмен будет очевидным, если у одной учетной записи одновременно есть логины от разных интернет-провайдеров в разных странах. Обратите внимание, что просто наличие другого IP-адреса не является достаточным основанием для того, чтобы считать его общим - некоторые интернет-провайдеры используют прокси-серверы с циклическим циклом, поэтому каждый удар обязательно будет другим IP-адресом.
Вот как я решил проблему с дублирующимся сеансом.
routes.rb
devise_for :users, :controllers => { :sessions => "my_sessions" }
my_sessions controller
class MySessionsController < Devise::SessionsController
skip_before_filter :check_concurrent_session
def create
super
set_login_token
end
private
def set_login_token
token = Devise.friendly_token
session[:token] = token
current_user.login_token = token
current_user.save(validate: false)
end
end
application_controller
def check_concurrent_session
if duplicate_session?
sign_out_and_redirect(current_user)
flash[:notice] = "Duplicate Login Detected"
end
end
def duplicate_session?
user_signed_in? && (current_user.login_token != session[:token])
end
Модель пользователя Добавьте строковое поле с помощью перенаправления с именем login_token
. Это переопределяет контроллер Devise Session по умолчанию, но также наследует его. На новом сеансе в пользовательской модели создается и сохраняется в login_token
токен сеанса входа. В контроллере приложения мы вызываем check_concurrent_session
, который выдает и перенаправляет current_user
после вызова функции duplicate_session?
.
Это не самый чистый способ сделать это, но он определенно работает.
Этот драгоценный камень хорошо работает: https://github.com/phatworx/devise_security_extension
Добавить в Gemfile
gem 'devise_security_extension'
после установки пакета
rails g devise_security_extension:install
Затем запустите
rails g migration AddSessionLimitableToUsers unique_session_id
Отредактируйте файл миграции
class AddSessionLimitableToUsers < ActiveRecord::Migration
def change
add_column :users, :unique_session_id, :string, limit: 20
end
end
Затем запустите
rake db:migrate
Отредактируйте приложение /models/user.rb file
class User < ActiveRecord::Base
devise :session_limitable # other devise options
... rest of file ...
end
Выполнено. Теперь вход в другой браузер приведет к удалению любых предыдущих сеансов. Драгоценный камень фактически уведомляет пользователя о том, что он собирается убить текущий сеанс перед входом в систему.
session_limitable
для обновления предыдущего сеанса. Можете ли вы объяснить, почему вы хотите обновить предыдущий сеанс, если вы обращаетесь к сайту из нового браузера? Для правильного ответа на ваш вопрос требуется более подробная информация.
– scarver2
3 September 2017 в 19:32