web-dev-qa-db-ja.com

ユーザーごとに一度に1つのセッションに制限を設ける

私のアプリは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
51
John

できません。

  • ユーザーのIPアドレスを制御できるため、一度に2つのIPからユーザーの存在を防ぐことができます。また、ログインとIPをバインドできます。ユーザーをブロックするために、IPを介して都市やその他の地理位置情報データを確認することができます。
  • Cookieを設定して、他のものを制御できます。

しかし、これだけでは、1人のユーザーだけがこのログインを使用すること、および世界中の105 IPが、プロキシなどを使用する1人の一意のユーザーだけに属していないことは保証されません。

そして最後に、これはインターネットでは必要ありません。

[〜#〜]更新[〜#〜]

しかし、私が求めているのは、複数のユーザーが同じアカウントを同時に使用できないようにすることです。

したがって、暗号化されたデータを含むトークンを保存できます:IP +秘密の文字列+ユーザーエージェント+ユーザーのブラウザーのバージョン+ユーザーのOS +その他の個人情報:encrypt(IP + "some secret string" + request.user_agent + ...)。そして、そのトークンを使用してセッションまたはCookieを設定できます。そして、各リクエストでそれをフェッチすることができます:ユーザーが同じなら?同じOSの同じブラウザや同じブラウザバージョンを使用していますか。

また、動的トークンを使用することもできます。要求ごとにトークンを変更するため、セッションごとに1人のユーザーのみがシステムを使用できます。各要求トークンが変更されるため、別のユーザーは、トークンが期限切れになるまでログアウトされます。

10
fl00r

この宝石はうまく機能します: 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は、ログインする前に現在のセッションを終了しようとしていることをユーザーに通知します。

30
scarver2

これが、重複セッションの問題を解決する方法です。

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?をリダイレクトします。

それはそれについて行く最もきれいな方法ではありませんが、それは間違いなく動作します。

3
nulltek

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 
2
Dex

元の投稿での解決策は私にとってはうまくいかないことがわかりました。最初のユーザーをログアウトさせ、ログインページを表示したかったのです。また、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はこれを行う機能も提供しているようです。また、何が起こったのかを最初のユーザーに警告する適切なアラートも表示します(その方法はまだわかりません)。

1
davidm

ユーザーごとに使用されるuniq IPを追跡します。時々、これらのIPで分析を実行します。1つのアカウントが異なる国の異なるISPから同時にログインしている場合、共有は明らかです。単に異なるIPを持っているだけでは、それが共有されていると考えるのに十分ではないことに注意してください。一部のISPはラウンドロビンプロキシを使用しているため、各ヒットは必ず異なるIPになります。

0
Marc B

ユーザーがアカウントを共有することを確実に防ぐことはできませんが、できることは(私が思うに)、複数のユーザーが同じアカウントに同時にログオンすることを防ぐことです。これがビジネスモデルに十分かどうかはわかりませんが、他の回答で説明されている多くの問題を回避できます。私は現在ベータ版であり、かなりうまく機能しているように見えるものを実装しました-いくつかの注意事項があります ここ

0
chrispanda