验证失败后设计日志

Alm*_*ron 10 authentication devise ruby-on-rails-3.2

当有人试图登录我的应用程序(跟踪暴力尝试)时,我需要写一个日志.我还决定记录成功的身份验证.所以我创建了一个SessionsController <Devise :: SessionsController并试图覆盖会话#create方法,如:https://gist.github.com/3884693

第一部分完美地运行,但是当auth failes rails抛出某种异常并且永远不会到达if语句.所以我不知道该怎么办.

Pra*_*thy 19

对以前的SO问题的答案 - 设计:注册登录尝试有答案.

设计控制器中的create操作调用warden.authenticate !,它尝试使用提供的参数对用户进行身份验证.如果验证失败,则验证!将调用设计失败应用程序,然后运行SessionsController #new动作.请注意,如果身份验证失败,您对create操作的任何过滤器都将无法运行.

因此,解决方案是在新操作之后添加一个过滤器,用于检查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
Run Code Online (Sandbox Code Playgroud)

该日志包含以下条目:

要成功登录

Started POST "/users/sign_in"
...
...
***
Successful login with email_id : {"email"=>...
***
...
...
Completed 302 Found
Run Code Online (Sandbox Code Playgroud)

登录失败

Started POST "/users/sign_in"
...
...
Completed 401 Unauthorized 
Processing by SessionsController#new as HTML
...
...
***
Failed login with email_id : {"email"=>...
***
...
...
Completed 302 Found
Run Code Online (Sandbox Code Playgroud)


Gra*_*eme 7

我有同样的问题,但无法解决它,"warden.options"因为在我的情况下,这些在重定向到sessions#new操作之前已被清除。寻找到几个选择,我判断是太脆(因为涉案延长一些设计类和走样现有方法)后,我结束了使用一些提供的回调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
Run Code Online (Sandbox Code Playgroud)

我希望您也可以使用before_logout回调来跟踪注销操作,但我尚未对其进行测试。似乎也有prepend_回调的变体。


Max*_*ace 5

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
Run Code Online (Sandbox Code Playgroud)

如果您想了解Warden的回调,请查看Graeme的答案(Devise是使用Warden实现的)。