2013-07-22 37 views
15

私はDeviseでRoRアプリケーションを開発中です。私はクライアントがサーバにリクエストを送信して、クライアント上のユーザが(Timeoutableモジュールを使用して)非アクティブのために自動的にログアウトするまでの時間を確認したいとします。私は、ユーザーがログオフするまでDeviseにカウントダウンをリセットさせるよう要求したくありません。これをどうすれば設定できますか? RoRのは、認証されたユーザからの要求を受け取るたびに、Timeoutable#timeout_in私はDevise#timeout_inに設定されているものは何でもにリセットされますので、Devise - 非アクティブのためにユーザーがログアウトするまで、カウントダウンをリセットせずにリクエストを行います。

class SessionTimeoutController < ApplicationController 
    before_filter :authenticate_user! 

    # Calculates the number of seconds until the user is 
    # automatically logged out due to inactivity. Unlike most 
    # requests, it should not reset the timeout countdown itself. 
    def check_time_until_logout 
    @time_left = current_user.timeout_in 
    end 

    # Determines whether the user has been logged out due to 
    # inactivity or not. Unlike most requests, it should not reset the 
    # timeout countdown itself. 
    def has_user_timed_out 
    @has_timed_out = current_user.timedout? (Time.now) 
    end 

    # Resets the clock used to determine whether to log the user out 
    # due to inactivity. 
    def reset_user_clock 
    # Receiving an arbitrary request from a client automatically 
    # resets the Devise Timeoutable timer. 
    head :ok 
    end 
end 

SessionTimeoutController#reset_user_clock作品:

これは私が今持っているコードです。リセットを防ぐにはcheck_time_until_logouthas_user_timed_out

答えて

19

私は自分のコードにいくつかの変更を加えることになりました。私はそれが何をするかを説明し、私が終わったとき、私はなってしまったものを紹介します:これらは私が行った変更されている

class SessionTimeoutController < ApplicationController 
    # These are what prevent check_time_until_logout and 
    # reset_user_clock from resetting users' Timeoutable 
    # Devise "timers" 
    prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] 
    def skip_timeout 
    request.env["devise.skip_trackable"] = true 
    end 

    skip_before_filter :authenticate_user!, only: [:has_user_timed_out] 

    def check_time_until_logout 
    @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round 
    end 

    def has_user_timed_out 
    @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) 
    end 

    def reset_user_clock 
    # Receiving an arbitrary request from a client automatically 
    # resets the Devise Timeoutable timer. 
    head :ok 
    end 
end 

env["devise.skip_trackable"]

を使用してこれがコードです

prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] 
    def skip_timeout 
    request.env["devise.skip_trackable"] = true 
    end 

これを:それはそれは不活動によるアウトをユーザーにログインする前に待機する時間をリセットするから工夫を防止しますコードは、ユーザーが最後に活動したときに追跡するために保存する値を更新するかどうかを決定するために、Deviseが内部的に使用するハッシュ値を変更します。具体的には、これは我々が(link)と相互に作用している工夫コードです:

Warden::Manager.after_set_user do |record, warden, options| 
    scope = options[:scope] 
    env = warden.request.env 

    if record && record.respond_to?(:timedout?) && warden.authenticated?(scope) && options[:store] != false 
    last_request_at = warden.session(scope)['last_request_at'] 

    if record.timedout?(last_request_at) && !env['devise.skip_timeout'] 
     warden.logout(scope) 
     if record.respond_to?(:expire_auth_token_on_timeout) && record.expire_auth_token_on_timeout 
     record.reset_authentication_token! 
     end 
     throw :warden, :scope => scope, :message => :timeout 
    end 

    unless env['devise.skip_trackable'] 
     warden.session(scope)['last_request_at'] = Time.now.utc 
    end 
    end 
end 

(このコードはRailsは、クライアントからの要求を処理するたびに実行されることに注意してください。)

近くこれらの行

unless env['devise.skip_trackable'] 
     warden.session(scope)['last_request_at'] = Time.now.utc 
    end 

これは、ユーザーが非アクティブなためにログアウトするまでカウントダウンを「リセット」するコードです。 env['devise.skip_trackable']trueでない場合にのみ実行されるため、Deviseがユーザーの要求を処理する前にその値を変更する必要があります。

これを行うには、何かをする前にenv['devise.skip_trackable']の値を変更するようにRailsに指示します。ここでも、私の最終的なコードから:この点以上の

prepend_before_action :skip_timeout, only: [:check_time_until_logout, :has_user_timed_out] 
    def skip_timeout 
    request.env["devise.skip_trackable"] = true 
    end 

すべてが、私は私の質問に答えるために変更するために必要なものです。しかし、私が望むようにコードを動作させるために必要ないくつかの変更がありましたので、ここでもそれらを説明します。私の質問の私のコードは、カップル、他の問題を抱えているようTimeoutable正しく

を使用して

私は、Timeoutableモジュールに関するドキュメントを読み違えます。

まず、私のcheck_time_until_logoutメソッドは常に同じ値を返します。これは私が持っていたアクションの誤ったバージョンです:

def check_time_until_logout 
    @time_left = current_user.timeout_in 
end 

私は、ユーザーが自動的にログアウトされるまでTimeoutable#timeout_inは時間の量を返すだろうと思いました。代わりに、ユーザーをログアウトする前にDeviseが待機するように構成されている時間を返します。ユーザーがどれくらい長く自分を去ったかを計算する必要があります。

これを計算するには、ユーザーが最後にDeviseが認識したアクティビティがいつあったかを知る必要があります。このコードは、我々は上記の見工夫ソースから、ユーザーが最後にアクティブだったときに決定されます。私たちはwarden.session(scope)によって返されたオブジェクトへのハンドルを取得する必要があり

last_request_at = warden.session(scope)['last_request_at'] 

。これは、ハンドルcurrent_useruser_signed_in?のように、Deviseが私たちに提供するuser_sessionハッシュのようなものです。

user_sessionハッシュを使用し、自分自身を残り時間を算出する、check_time_until_logout方法は私もTimeoutable#timedout?のドキュメントを読み違え

def check_time_until_logout 
    @time_left = Devise.timeout_in - (Time.now - user_session["last_request_at"]).round 
    end 

なります。ユーザーが最後にアクティブだった時間に渡すときにユーザーがタイムアウトしたかどうかを確認するために、は現在時刻を渡すときにではありません。私たちが作る必要がある変更は簡単です:私はこれらの3つの変更を行ったら、私のコントローラは、私はそれを期待する方法を務めた

def has_user_timed_out 
    @has_timed_out = (!current_user) or (current_user.timedout? (user_session["last_request_at"])) 
    end 

:代わりにTime.nowに渡すので、私たちはuser_sessionハッシュの時間に渡す必要があります。

+2

良い答え、コードサンプルありがとう!レール4.2.6でこのコードを使用し、3.5.6を考案しましたが、修正するためにいくつかのエラーがありました。 'NoMethodError Exception:未定義のメソッド' - @ ''は、@time_left = Devise.timeout_in - (Time.now - user_session [" last_request_at "])のroundを' @time_left = Devise.timeout_in - (Time。 to_i' – Cameron

+0

'@has_timed_out =(!current_user)または(current_user.timedout?)という行を変更すると、' ArgumentError Exception:ActiveSupport :: TimeWithZoneとのFixnumの比較に失敗しました 'というエラーが修正されました。 – Cameron

+0

クライアントにアラートメッセージを送信する方法(user_session ["last_request_at"])) '' @has_timed_out =(!current_user)または(current_user.timedout?(Time.at(user_session ["last_request_at"])))前にタイムアウトを工夫しますか? –

15

私はこれを正しく(おそらく、機種によって異なるが)見ると、Timeoutableモジュールを通してログアウトを処理する。

これはラックミドルウェアスタックの一部である監視員に接続されています。あなたはこの部分を見つけることができるよりも

あなたはコードを見れば、:

unless warden.request.env['devise.skip_trackable'] 
    warden.session(scope)['last_request_at'] = Time.now.utc 
end 

ので、私はここで見ることができるものから、あなたは監視員のミドルウェア取得者と呼ばれる前にtrueにdevise.skip_trackableを設定することができるはずです。

私はここに、この問題は、実際にそれを使用する方法について説明していることを考える:https://github.com/plataformatec/devise/issues/953

+0

あなたの答えが私に何をしているのか把握するのに必要なプッシュを与えたので、私はあなたに賞金を与えます。私は自分の問題にどのように対処するのかを正確に示すコードサンプルを提供しているので、今後の読者にとってはより有用になると思うので、私の答えを受け入れています。ご協力ありがとうございました! = D – Kevin

2

request.env["devise.skip_trackable"] = trueアクションの先頭にある2つの主な回答は、当初私にとってはうまくいきませんでした。

これは、従来の "authenticate_user!"アクションを使用していて、認証されたルートを使用していない場合にのみ動作するようです。あなたのような、あなたのconfig/routes.rbで認証ルートを使用している場合:

authenticate :user do 
    resources :some_resources 
end 

は、アクションを付加することは、時間での工夫チェーンに影響を与えていないようですので、あなたは、authenticateブロックをコメントアウトする代わりに、アクションの前authenticate_user!を使用する必要がありますapp/controllers/application_controller.rbに追加し、特定のコントローラーでskip_trackableをtrueに設定する前置アクションを追加します。

関連する問題