Authentication 身份验证失败后设计日志
当有人登录我的应用程序失败时,我需要写一个日志(跟踪暴力尝试)。我还决定记录成功的身份验证。 因此,我创建了一个sessioncontrollerAuthentication 身份验证失败后设计日志,authentication,devise,ruby-on-rails-3.2,Authentication,Devise,Ruby On Rails 3.2,当有人登录我的应用程序失败时,我需要写一个日志(跟踪暴力尝试)。我还决定记录成功的身份验证。 因此,我创建了一个sessioncontroller
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
对于失败的登录
对于注销日志记录,您需要捕获销毁事件,因此将以下内容添加到会话控制器(从上面的答案):
我有同样的问题,但无法使用
“warden.options”
解决,因为在我的情况下,在重定向到会话#new
操作之前,这些选项已被清除。在研究了一些我认为过于脆弱的备选方案之后(因为它们涉及扩展一些Desive类和对现有方法进行别名),我最终使用了一些。它对我来说效果更好,因为回调是在当前请求-响应周期内调用的,并且参数都保存在env
对象中
这些回调被命名,似乎是为了解决此问题和相关问题而设计的。而且它们是有文件记录的
Warden从Warden-1.2.3
起支持以下回调:
- 设置用户后的
- 认证后的
(用于记录成功登录) - 获取之后的
(设置用户之后的
别名)
(用于记录失败的登录-下面的示例)发生故障之前
- 获取失败后的
注销前
应请求
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
我希望您可以在退出之前使用回调来跟踪退出操作,但我还没有测试它。回调的变体似乎也有prepend
。是有帮助的,但依靠sessioncontroller#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
class LogAuthenticationFailure
查看您是否愿意挂接Warden的回调(Desive是使用Warden实现的)。我找到了另一种方法,例如,如果您希望,在登录失败时显示自定义消息
在我的工作中,如果登录失败,我们将检查活动状态(自定义逻辑)并显示消息,无论登录是否正确
在调试了一点并阅读了warden文档之后,我现在知道了:warden执行一个抛出(:warden,opts)
,因此,根据ruby文档,必须在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
抛出文档:
捕获文档:
基于Prakash Murty的答案,我认为这个答案()中的方法是记录成功登录尝试的更干净的方法。designe提供了一种在渲染视图之前传递yield
ed的块的方法,而不是调用super
因此,与其这样做,不如:
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 sessioncontroller
这样做比较干净:
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
class sessioncontroller
我还按照上的Desive指南操作了其余的代码。我不得不将其更改为一个符号:选项[:操作]=未经认证的还有一种方法来捕获失败的登录尝试,因为用户试图登录未经确认的帐户?有一点要考虑的是,这种方法还记录访问未经验证的路线…您可以添加另一个筛选器以确保它仅在登录尝试时使用:如果env.dig(“warden.options”),:action)=“unauthenticated”&&env.dig(“warden.options”),:message)=:invalid
在Rails 5.1中,我收到了弃用警告:env已弃用,将从Rails 5.1中删除。谢谢您的评论!我将env
更新为request.env
per。如果您仍然看到弃用或存在任何其他问题,请告诉我。是否有任何参数传递给Devise::FailureApp
。换句话说,我能从尝试登录的用户中提取一些信息吗?@bo oz我已经很长时间没有使用此代码了(无法验证),但请尝试深入到request.env?您应该能够获得会话信息。似乎不再需要额外的数据库命中。当在数据库中找不到用户时,opts[:message]
被设置为:not\u found\u in\u database
,这太好了。谢谢
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
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