Устанавливать ограничение на один сеанс на пользователя за раз
Мое приложение использует Rails 3.0.4 и Devise 1.1.7.
Я ищу способ предотвратить использование пользователями учетных записей, поскольку приложение является услугой, основанной на подписке. Я искал более недели, и я до сих пор не знаю, как реализовать решение. Я надеюсь, что кто-то выполнит решение и может указать мне в правильном направлении.
Решение (Спасибо всем за ваши ответы и понимание!)
В приложении controller.rb
before_filter :check_concurrent_session
def check_concurrent_session
if is_already_logged_in?
sign_out_and_redirect(current_user)
end
end
def is_already_logged_in?
current_user && !(session[:token] == current_user.login_token)
end
В session_controller, который переопределяет контроллер Devise Sessions:
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
end
В миграции AddLoginTokenToUsers
def self.up
change_table "users" do |t|
t.string "login_token"
end
end
def self.down
change_table "users" do |t|
t.remove "login_token"
end
end
Ответы
Ответ 1
Вы не можете этого сделать.
- Вы можете управлять IP-адресами пользователя, поэтому вы можете запретить присутствие пользователя из двух IP за раз. И вы можете связать логин и IP-адрес. Вы можете попытаться проверить города и другие данные геолокации через IP, чтобы заблокировать пользователя.
- Вы можете установить файлы cookie для управления чем-то другим.
Но ничто из этого не гарантирует, что только один пользователь использует этот логин и что эти 105 IP со всего мира принадлежат не только одному уникальному пользователю, который использует прокси-сервер или что-то еще.
И последнее: вы никогда не нуждаетесь в этом в Интернете.
UPD
Тем не менее, я прошу ограничить использование нескольких пользователей одной и той же учетной записью одновременно, что, по моему мнению, должно быть возможным
Таким образом, вы можете сохранить некоторый токен, который будет содержать некоторые зашифрованные данные: IP + секретная строка + пользовательский агент + версия браузера пользователя + пользовательская ОС + любая другая личная информация: encrypt(IP + "some secret string" + request.user_agent + ...)
. А затем вы можете установить сеанс или файл cookie с этим токеном. И с каждым запросом вы можете его получить: если пользователь тот же? Использует ли он тот же браузер и ту же версию браузера из той же ОС и т.д.
Также вы можете использовать динамические токены: вы меняете токен на каждый запрос, поэтому только один пользователь может использовать систему за сеанс, поскольку каждый токен запроса будет изменен, другой пользователь будет выведен из системы, пока его токен будет истек.
Ответ 2
Этот драгоценный камень хорошо работает: 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
class User < ActiveRecord::Base
devise :session_limitable # other devise options
... rest of file ...
end
Готово. Теперь вход в другой браузер приведет к удалению любых предыдущих сеансов. Драгоценный камень фактически уведомляет пользователя о том, что он собирается убить текущий сеанс перед входом в систему.
Ответ 3
Что касается фактического внедрения в 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
Ответ 4
Вот как я решил проблему с дублирующимся сеансом.
routes.rb
devise_for :users, :controllers => { :sessions => "my_sessions" }
Контроллер my_sessions
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
в модели User. В контроллере приложения мы вызываем check_concurrent_session
, который выдает и перенаправляет current_user
после вызова функции duplicate_session?
.
Это не самый чистый способ сделать это, но он определенно работает.
Ответ 5
Я обнаружил, что решение в оригинальной публикации не работает для меня. Я хотел, чтобы первый пользователь вышел из системы и на странице входа. Кроме того, метод 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
, как представляется, предоставляет функциональные возможности для этого. Он также устанавливает соответствующее предупреждение, предупреждающее первого пользователя о том, что произошло (я еще не понял, как это сделать).
Ответ 6
Отслеживайте IP-адреса uniq, используемые для каждого пользователя. Время от времени выполняйте анализ этих IP-адресов - обмен будет очевидным, если у одной учетной записи одновременно есть логины от разных интернет-провайдеров в разных странах. Обратите внимание, что просто наличие другого IP-адреса не является достаточным основанием для того, чтобы считать его общим - некоторые интернет-провайдеры используют прокси-серверы с циклическим циклом, поэтому каждый удар обязательно будет другим IP-адресом.
Ответ 7
Пока вы не можете надежно запретить пользователям делиться учетной записью, то, что вы можете сделать (я думаю), предотвращает одновременную регистрацию одного пользователя в одной учетной записи. Не уверен, что этого достаточно для вашей бизнес-модели, но она затрагивает множество проблем, обсуждаемых в других ответах. Я реализовал что-то, что в настоящее время находится в бета-версии, и, похоже, работает достаточно хорошо - есть некоторые примечания здесь