11

誰かが私のアプリにログインできなかった場合(ブルートフォースの試みを追跡するため)、ログを書き込む必要があります。また、成功した認証をログに記録することにしました。そこで、SessionsController <Devise :: SessionsControllerを作成し、次のようにsessions#createメソッドをオーバーライドしようとしました:https ://gist.github.com/3884693

最初の部分は完全に機能しますが、認証が失敗すると、railsはある種の例外をスローし、ifステートメントに到達しません。だから私は何をすべきかわかりません。

4

6 に答える 6

26

前のSOの質問に対するこの回答-工夫:ログイン試行の登録には答えがあります。

deviseコントローラーのcreateアクションは、warden.authenticate!を呼び出します。これは、指定されたパラメーターを使用してユーザーを認証しようとします。認証に失敗した場合は、認証してください。devise失敗アプリを呼び出し、次にSessionsController#newアクションを実行します。認証が失敗した場合、作成アクション用のフィルターは実行されないことに注意してください。

したがって、解決策は、env ["warden.options"]の内容をチェックし、適切なアクションを実行する新しいアクションの後にフィルターを追加することです。

私は提案を試し、成功したログイン試行と失敗したログイン試行の両方をログに記録することができました。関連するコントローラーコードは次のとおりです。

class SessionsController < Devise::SessionsController
  after_filter :log_failed_login, :only => :new

  def create
    super
    ::Rails.logger.info "\n***\nSuccessful login with email_id : #{request.filtered_parameters["user"]}\n***\n"
  end

  private
  def log_failed_login
    ::Rails.logger.info "\n***\nFailed login with email_id : #{request.filtered_parameters["user"]}\n***\n" if failed_login?
  end 

  def failed_login?
    (options = env["warden.options"]) && options[:action] == "unauthenticated"
  end 
end

ログには次のエントリがあります。

ログインを成功させるには

Started POST "/users/sign_in"
...
...
***
Successful login with email_id : {"email"=>...
***
...
...
Completed 302 Found

ログインに失敗した場合

Started POST "/users/sign_in"
...
...
Completed 401 Unauthorized 
Processing by SessionsController#new as HTML
...
...
***
Failed login with email_id : {"email"=>...
***
...
...
Completed 302 Found
于 2012-10-13T21:30:59.513 に答える
8

Prakashの答えSessionsController#newは役に立ちますが、副作用として実行されることに依存することは理想的ではありません。私はこれがよりきれいだと信じています:

class LogAuthenticationFailure < Devise::FailureApp
  def respond
    if request.env.dig('warden.options', :action) == 'unauthenticated'
      Rails.logger.info('...')
    end
    super
  end
end

...

Devise.setup do |config|

config.warden do |manager|
  manager.failure_app = LogAuthenticationFailure
end

Wardenのコールバックにフックしたい場合は、Graemeの回答を確認してください(DeviseはWardenを使用して実装されています)。

于 2016-08-18T12:12:47.110 に答える
7

同じ質問がありましたが、私の場合、アクション"warden.options"にリダイレクトする前にこれらがクリアされていたため、使用して解決できませんでした。sessions#new脆弱すぎると判断したいくつかの代替案を調べた後(いくつかのDeviseクラスの拡張と既存のメソッドのエイリアス化が含まれていたため)、によって提供されるいくつかのコールバックWardenを使用することになりました。コールバックは現在の要求/応答サイクル内で呼び出され、パラメーターはすべてオブジェクトに保持されるため、私にとってはより適切に機能しenvます。

これらのコールバックには名前が付けられており、この問題および関連する問題を解決するように設計されているようです。そして、それらは文書化されています!

Wardenは、現在、次のコールバックをサポートしていますwarden-1.2.3

  • after_set_user
  • after_authentication (正常なサインインのログ記録に役立ちます)
  • after_fetch(のエイリアスafter_set_user
  • before_failure(失敗したサインインのログ記録に役立ちます-以下の例)
  • after_failed_fetch
  • before_logout
  • on_request

各コールバックはWarden::Managerクラスに直接設定されます。失敗した認証の試行を追跡するために、これを追加しました。

Warden::Manager.before_failure do |env, opts|
  email = env["action_dispatch.request.request_parameters"][:user] &&
          env["action_dispatch.request.request_parameters"][:user][:email]
  # unfortunately, the User object has been lost by the time 
  # we get here; so we take a db hit because I care to see 
  # if the email matched a user account in our system
  user_exists = User.where(email: email).exists?

  if opts[:message] == :unconfirmed
    # this is a special case for me because I'm using :confirmable
    # the login was correct, but the user hasn't confirmed their 
    # email address yet
    ::Rails.logger.info "*** Login Failure: unconfirmed account access: #{email}"
  elsif opts[:action] == "unauthenticated"
    # "unauthenticated" indicates a login failure
    if !user_exists
      # bad email:
      # no user found by this email address
      ::Rails.logger.info "*** Login Failure: bad email address given: #{email}"
    else
      # the user exists in the db, must have been a bad password
      ::Rails.logger.info "*** Login Failure: email-password mismatch: #{email}"
    end
  end
end

コールバックを使用してbefore_logoutログアウトアクションも追跡できると思いますが、テストしていません。prepend_コールバックのバリエーションもあるようです。

于 2015-10-20T07:30:08.300 に答える
1

ログアウトロギングの場合、destroyイベントをキャッチする必要があるため、以下をセッションコントローラーに追加します(上記の回答から)。

before_filter :log_logout, :only => :destroy  #add this at the top with the other filters

def log_logout
     ::Rails.logger.info "*** Logging out : #{current_user.email} ***\n"  
end
于 2013-10-19T19:34:40.590 に答える
1

これを行う別の方法を見つけました。たとえば、ログインに失敗した場合にカスタムメッセージを表示したい場合です。

私の仕事では、ログインに失敗した場合、ログインが正しいかどうかに関係なく、アクティビティステータス(カスタムロジック)を確認してメッ​​セージを表示します。

少しデバッグしてwardenのドキュメントを読んだ後、私はこれを知っています。Wardenはを実行するthrow(:warden, opts)ので、rubyのドキュメントによると、ブロックthrow内にキャプチャする必要があります。catch

def create
  flash.clear
  login_result = catch(:warden) { super }
  return unless login_failed?(login_result)

  email = params[:user][:email]
  flash[:alert] = # here I call my service that calculates the message
  redirect_to new_user_session_path
end

def login_failed?(login_result)
  login_result.is_a?(Hash) && login_result.key?(:scope) && login_result.key?(:recall)
end

ドキュメントを投げる: https ://ruby-doc.org/core-2.6.3/Kernel.html#method-i-throw

キャッチドキュメント: https ://ruby-doc.org/core-2.6.3/Kernel.html#method-i-catch

于 2019-07-05T14:30:45.227 に答える
0

Prakash Murtyの回答に基づいて、この回答( https://stackoverflow.com/a/34816998/891359)のアプローチは、成功したログイン試行をログに記録するためのよりクリーンな方法だと思います。スーパーを呼び出す代わりに、Deviseはyield、ビューがレンダリングされる前に編集されたブロックを渡す方法を提供します。

したがって、これを行う代わりに:

class SessionsController < Devise::SessionsController
  def create
    super
    ::Rails.logger.info "\n***\nSuccessful login with email_id : #{request.filtered_parameters["user"]}\n***\n"
  end
end

行う方がクリーンです:

class SessionsController < Devise::SessionsController
  def create
    super do |user|
      ::Rails.logger.info "\n***\nSuccessful login with email_id : #{user.email}\n***\n"
    end
  end
end
于 2020-03-10T07:07:00.187 に答える