私のアプリはRails 3.0.4およびDevise 1.1.7を使用しています。
アプリはサブスクリプションベースのサービスであるため、ユーザーがアカウントを共有できないようにする方法を探しています。私は1週間以上探していましたが、まだソリューションを実装する方法がわかりません。誰かが解決策を実装してくれて、正しい方向に向けてくれることを願っています。
ソリューション(皆さんの回答と洞察に感謝します!)
アプリケーション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
Devise Sessionsコントローラーをオーバーライドするsession_controllerで:
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人のユーザーだけがこのログインを使用すること、および世界中の105 IPが、プロキシなどを使用する1人の一意のユーザーだけに属していないことは保証されません。
そして最後に、これはインターネットでは必要ありません。
[〜#〜]更新[〜#〜]
しかし、私が求めているのは、複数のユーザーが同じアカウントを同時に使用できないようにすることです。
したがって、暗号化されたデータを含むトークンを保存できます:IP +秘密の文字列+ユーザーエージェント+ユーザーのブラウザーのバージョン+ユーザーのOS +その他の個人情報:encrypt(IP + "some secret string" + request.user_agent + ...)
。そして、そのトークンを使用してセッションまたはCookieを設定できます。そして、各リクエストでそれをフェッチすることができます:ユーザーが同じなら?同じOSの同じブラウザや同じブラウザバージョンを使用していますか。
また、動的トークンを使用することもできます。要求ごとにトークンを変更するため、セッションごとに1人のユーザーのみがシステムを使用できます。各要求トークンが変更されるため、別のユーザーは、トークンが期限切れになるまでログアウトされます。
この宝石はうまく機能します: https://github.com/devise-security/devise-security
Gemfileに追加
gem 'devise-security'
バンドルのインストール後
Rails generate devise_security: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
App/models/user.rbファイルを編集します
class User < ActiveRecord::Base
devise :session_limitable # other devise options
... rest of file ...
end
できました。別のブラウザからログインすると、以前のセッションがすべて終了します。 gem actualは、ログインする前に現在のセッションを終了しようとしていることをユーザーに通知します。
これが、重複セッションの問題を解決する方法です。
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コントローラをオーバーライドしますが、それからも継承します。新しいセッションでは、ログインセッショントークンが作成され、Userモデルのlogin_token
に保存されます。アプリケーションコントローラーでcheck_concurrent_session
を呼び出します。これにより、current_user
関数を呼び出した後にサインアウトしてduplicate_session?
をリダイレクトします。
それはそれについて行く最もきれいな方法ではありませんが、それは間違いなく動作します。
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
元の投稿での解決策は私にとってはうまくいかないことがわかりました。最初のユーザーをログアウトさせ、ログインページを表示したかったのです。また、sign_out_and_redirect(current_user)
メソッドは、期待したとおりに機能しないようです。そのソリューションでSessionsControllerオーバーライドを使用して、次のようにwebsocketを使用するように変更しました。
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
すべてのWebページがサインアウトチャネルにサブスクライブし、同じlogout_subscribe_address
アクションにバインドされていることを確認してください。私のアプリケーションでは、各ページに「サインアウト」ボタンがあり、工夫セッションの破棄アクションを介してクライアントからサインアウトします。 WebSocket応答がWebページでトリガーされると、このボタンをクリックするだけです。サインアウトロジックが呼び出され、最初のユーザーにサインインページが表示されます。
このソリューションは、skip_before_filter :check_concurrent_session
とモデルlogin_token
も必要としません。偏見なく強制ログアウトがトリガーされるためです。
記録として、devise_security_extension
はこれを行う機能も提供しているようです。また、何が起こったのかを最初のユーザーに警告する適切なアラートも表示します(その方法はまだわかりません)。
ユーザーごとに使用されるuniq IPを追跡します。時々、これらのIPで分析を実行します。1つのアカウントが異なる国の異なるISPから同時にログインしている場合、共有は明らかです。単に異なるIPを持っているだけでは、それが共有されていると考えるのに十分ではないことに注意してください。一部のISPはラウンドロビンプロキシを使用しているため、各ヒットは必ず異なるIPになります。
ユーザーがアカウントを共有することを確実に防ぐことはできませんが、できることは(私が思うに)、複数のユーザーが同じアカウントに同時にログオンすることを防ぐことです。これがビジネスモデルに十分かどうかはわかりませんが、他の回答で説明されている多くの問題を回避できます。私は現在ベータ版であり、かなりうまく機能しているように見えるものを実装しました-いくつかの注意事項があります ここ