设计可锁定设置不起作用(使用 Docker)

发布于 2025-01-09 09:40:58 字数 9383 浏览 0 评论 0原文

我是初级开发者。我最近开始了我的第一份工作,在几次登录尝试失败后,我必须实施帐户锁定。

Devise 已在项目中实施。我已在我的用户模型上设置了“可使用设计锁定”。

在特定时间内输入错误的凭据后,帐户仍处于活动状态,并且不会记录 failed_attempts。

我已遵循本指南[此处]

我一直在阅读 Devise 文档以及各种 StackOverflow 问题和答案,但我无法解决我的问题。

这是我当前的设置:

devise.rb


  # ==> Configuration for :lockable
  # Defines which strategy will be used to lock an account.
  # :failed_attempts = Locks an account after a number of failed attempts to sign in.
  # :none            = No lock strategy. You should handle locking by yourself.
  config.lock_strategy = :failed_attempts

  # Defines which strategy will be used to unlock an account.
  # :email = Sends an unlock link to the user email
  # :time  = Re-enables login after a certain amount of time (see :unlock_in below)
  # :both  = Enables both strategies
  # :none  = No unlock strategy. You should handle unlocking by yourself.
  # config.unlock_strategy = :both
  config.unlock_strategy = :time

  # Number of authentication tries before locking an account if lock_strategy
  # is failed attempts.
  # config.maximum_attempts = 20
  config.maximum_attempts = 5

  # Time interval to unlock the account if :time is enabled as unlock_strategy.
  config.unlock_in = 1.hour

user.rb

class User < ActiveRecord::Base
  devise :invitable, :rememberable, :database_authenticatable, :registerable,
         :recoverable, :trackable, :validatable,
         :authentication_keys => [:login], :reset_password_keys => [:login],
         :cookie_domain => :all
         :lockable


xxxxxx_add_lockable_to_user.rb

class AddLockableToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :locked_at, :datetime
    add_column :users, :failed_attempts, :integer, default: 0, null: false

    execute("UPDATE users SET confirmed_at = NOW()")
  end

  def self.down
    remove_column :users, :failed_attempts
    remove_column :users, :locked_at
  end
end

Rails 控制台错误消息

irb(main):006:0> User.first.respond_to? :failed_attempts
=> true
irb(main):007:0> User.last.valid_for_authentication?
=> true
irb(main):008:0> User.last.access_locked?
NoMethodError: undefined method `access_locked?' for #<User:0x0000000d2a4a10>
        from /usr/local/bundle/gems/activemodel-3.0.20/lib/active_model/attribute_methods.rb:392:in `method_missing'
        from /usr/local/bundle/gems/activerecord-3.0.20/lib/active_record/attribute_methods.rb:46:in `method_missing'
        from (irb):8
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands/console.rb:44:in `start'
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands/console.rb:8:in `start'
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands.rb:23:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

我已在模型文件夹中添加了 lockable.rb,尽管我不认为这是必要的(是吗?)。

require "devise/hooks/lockable"

module Devise
  module Models
    # Handles blocking a user access after a certain number of attempts.
    # Lockable accepts two different strategies to unlock a user after it's
    # blocked: email and time. The former will send an email to the user when
    # the lock happens, containing a link to unlock it's account. The second
    # will unlock the user automatically after some configured time (ie 2.hours).
    # It's also possible to setup lockable to use both email and time strategies.
    #
    # == Options
    #
    # Lockable adds the following options to devise_for:
    #
    #   * +maximum_attempts+: how many attempts should be accepted before blocking the user.
    #   * +lock_strategy+: lock the user account by :failed_attempts or :none.
    #   * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
    #   * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both.
    #   * +unlock_keys+: the keys you want to use when locking and unlocking an account
    #
    module Lockable
      extend  ActiveSupport::Concern

      delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"

      # Lock a user setting it's locked_at to actual time.
      def lock_access!
        self.locked_at = Time.now

        if unlock_strategy_enabled?(:email)
          generate_unlock_token
          send_unlock_instructions
        end

        save(:validate => false)
      end

      # Unlock a user by cleaning locket_at and failed_attempts.
      def unlock_access!
        self.locked_at = nil
        self.failed_attempts = 0 if respond_to?(:failed_attempts=)
        self.unlock_token = nil  if respond_to?(:unlock_token=)
        save(:validate => false)
      end

      # Verifies whether a user is locked or not.
      def access_locked?
        !!locked_at && !lock_expired?
      end

      # Send unlock instructions by email
      def send_unlock_instructions
        ::Devise.mailer.unlock_instructions(self).deliver
      end

      # Resend the unlock instructions if the user is locked.
      def resend_unlock_token
        if_access_locked { send_unlock_instructions }
      end

      # Overwrites active? from Devise::Models::Activatable for locking purposes
      # by verifying whether a user is active to sign in or not based on locked?
      def active?
        super && !access_locked?
      end

      # Overwrites invalid_message from Devise::Models::Authenticatable to define
      # the correct reason for blocking the sign in.
      def inactive_message
        access_locked? ? :locked : super
      end

      # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
      # for verifying whether a user is allowed to sign in or not. If the user
      # is locked, it should never be allowed.
      def valid_for_authentication?
        puts "mark 1"
        return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
        puts "mark 2"

        # Unlock the user if the lock is expired, no matter
        # if the user can login or not (wrong password, etc)
        unlock_access! if lock_expired?

        case (result = super)
        when Symbol
          return result
        when TrueClass
          self.failed_attempts = 0
          save(:validate => false)
        when FalseClass
          # PostgreSQL uses nil as the default value for integer columns set to 0
          self.failed_attempts ||= 0
          self.failed_attempts += 1
          if attempts_exceeded?
            lock_access!
            return :locked
          else
            save(:validate => false)
          end
        end

        result
      end

      protected

        def attempts_exceeded?
          self.failed_attempts > self.class.maximum_attempts
        end

        # Generates unlock token
        def generate_unlock_token
          self.unlock_token = self.class.unlock_token
        end

        # Tells if the lock is expired if :time unlock strategy is active
        def lock_expired?
          if unlock_strategy_enabled?(:time)
            locked_at && locked_at < self.class.unlock_in.ago
          else
            false
          end
        end

        # Checks whether the record is locked or not, yielding to the block
        # if it's locked, otherwise adds an error to email.
        def if_access_locked
          if access_locked?
            yield
          else
            self.errors.add(:email, :not_locked)
            false
          end
        end

      module ClassMethods
        # Attempt to find a user by it's email. If a record is found, send new
        # unlock instructions to it. If not user is found, returns a new user
        # with an email not found error.
        # Options must contain the user email
        def send_unlock_instructions(attributes={})
         lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
         lockable.resend_unlock_token if lockable.persisted?
         lockable
        end

        # Find a user by it's unlock token and try to unlock it.
        # If no user is found, returns a new user with an error.
        # If the user is not locked, creates an error for the user
        # Options must have the unlock_token
        def unlock_access_by_token(unlock_token)
          lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
          lockable.unlock_access! if lockable.persisted?
          lockable
        end

        # Is the unlock enabled for the given unlock strategy?
        def unlock_strategy_enabled?(strategy)
          [:both, strategy].include?(self.unlock_strategy)
        end

        # Is the lock enabled for the given lock strategy?
        def lock_strategy_enabled?(strategy)
          self.lock_strategy == strategy
        end

        def unlock_token
          Devise.friendly_token
        end

        Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
      end
    end
  end
end

如果有人可以帮助指导我完成此设置,我将不胜感激。抱歉,如果问题多余或不清楚。

预先感谢您的帮助。

I am junior Dev. I have started my first job resently and i have to imprement Account Lockout after several failed login attempts.

Devise was already implemented in the project. i have set Lockable with Devise on my User model.

After entering wrong credentials for specific time the account is still active and it doesn't record failed_attempts.

I have followed this guide [HERE].

I've been reading over the Devise documentation and various StackOverflow questions and answers but I couln't fix my issue.

Here is my current setup:

devise.rb


  # ==> Configuration for :lockable
  # Defines which strategy will be used to lock an account.
  # :failed_attempts = Locks an account after a number of failed attempts to sign in.
  # :none            = No lock strategy. You should handle locking by yourself.
  config.lock_strategy = :failed_attempts

  # Defines which strategy will be used to unlock an account.
  # :email = Sends an unlock link to the user email
  # :time  = Re-enables login after a certain amount of time (see :unlock_in below)
  # :both  = Enables both strategies
  # :none  = No unlock strategy. You should handle unlocking by yourself.
  # config.unlock_strategy = :both
  config.unlock_strategy = :time

  # Number of authentication tries before locking an account if lock_strategy
  # is failed attempts.
  # config.maximum_attempts = 20
  config.maximum_attempts = 5

  # Time interval to unlock the account if :time is enabled as unlock_strategy.
  config.unlock_in = 1.hour

user.rb

class User < ActiveRecord::Base
  devise :invitable, :rememberable, :database_authenticatable, :registerable,
         :recoverable, :trackable, :validatable,
         :authentication_keys => [:login], :reset_password_keys => [:login],
         :cookie_domain => :all
         :lockable


xxxxxx_add_lockable_to_user.rb

class AddLockableToUser < ActiveRecord::Migration
  def self.up
    add_column :users, :locked_at, :datetime
    add_column :users, :failed_attempts, :integer, default: 0, null: false

    execute("UPDATE users SET confirmed_at = NOW()")
  end

  def self.down
    remove_column :users, :failed_attempts
    remove_column :users, :locked_at
  end
end

rails console error message

irb(main):006:0> User.first.respond_to? :failed_attempts
=> true
irb(main):007:0> User.last.valid_for_authentication?
=> true
irb(main):008:0> User.last.access_locked?
NoMethodError: undefined method `access_locked?' for #<User:0x0000000d2a4a10>
        from /usr/local/bundle/gems/activemodel-3.0.20/lib/active_model/attribute_methods.rb:392:in `method_missing'
        from /usr/local/bundle/gems/activerecord-3.0.20/lib/active_record/attribute_methods.rb:46:in `method_missing'
        from (irb):8
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands/console.rb:44:in `start'
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands/console.rb:8:in `start'
        from /usr/local/bundle/gems/railties-3.0.20/lib/rails/commands.rb:23:in `<top (required)>'
        from script/rails:6:in `require'
        from script/rails:6:in `<main>'

I have added lockable.rb in my model folder, even though i don't thing this is necessary (is it?).

require "devise/hooks/lockable"

module Devise
  module Models
    # Handles blocking a user access after a certain number of attempts.
    # Lockable accepts two different strategies to unlock a user after it's
    # blocked: email and time. The former will send an email to the user when
    # the lock happens, containing a link to unlock it's account. The second
    # will unlock the user automatically after some configured time (ie 2.hours).
    # It's also possible to setup lockable to use both email and time strategies.
    #
    # == Options
    #
    # Lockable adds the following options to devise_for:
    #
    #   * +maximum_attempts+: how many attempts should be accepted before blocking the user.
    #   * +lock_strategy+: lock the user account by :failed_attempts or :none.
    #   * +unlock_strategy+: unlock the user account by :time, :email, :both or :none.
    #   * +unlock_in+: the time you want to lock the user after to lock happens. Only available when unlock_strategy is :time or :both.
    #   * +unlock_keys+: the keys you want to use when locking and unlocking an account
    #
    module Lockable
      extend  ActiveSupport::Concern

      delegate :lock_strategy_enabled?, :unlock_strategy_enabled?, :to => "self.class"

      # Lock a user setting it's locked_at to actual time.
      def lock_access!
        self.locked_at = Time.now

        if unlock_strategy_enabled?(:email)
          generate_unlock_token
          send_unlock_instructions
        end

        save(:validate => false)
      end

      # Unlock a user by cleaning locket_at and failed_attempts.
      def unlock_access!
        self.locked_at = nil
        self.failed_attempts = 0 if respond_to?(:failed_attempts=)
        self.unlock_token = nil  if respond_to?(:unlock_token=)
        save(:validate => false)
      end

      # Verifies whether a user is locked or not.
      def access_locked?
        !!locked_at && !lock_expired?
      end

      # Send unlock instructions by email
      def send_unlock_instructions
        ::Devise.mailer.unlock_instructions(self).deliver
      end

      # Resend the unlock instructions if the user is locked.
      def resend_unlock_token
        if_access_locked { send_unlock_instructions }
      end

      # Overwrites active? from Devise::Models::Activatable for locking purposes
      # by verifying whether a user is active to sign in or not based on locked?
      def active?
        super && !access_locked?
      end

      # Overwrites invalid_message from Devise::Models::Authenticatable to define
      # the correct reason for blocking the sign in.
      def inactive_message
        access_locked? ? :locked : super
      end

      # Overwrites valid_for_authentication? from Devise::Models::Authenticatable
      # for verifying whether a user is allowed to sign in or not. If the user
      # is locked, it should never be allowed.
      def valid_for_authentication?
        puts "mark 1"
        return super unless persisted? && lock_strategy_enabled?(:failed_attempts)
        puts "mark 2"

        # Unlock the user if the lock is expired, no matter
        # if the user can login or not (wrong password, etc)
        unlock_access! if lock_expired?

        case (result = super)
        when Symbol
          return result
        when TrueClass
          self.failed_attempts = 0
          save(:validate => false)
        when FalseClass
          # PostgreSQL uses nil as the default value for integer columns set to 0
          self.failed_attempts ||= 0
          self.failed_attempts += 1
          if attempts_exceeded?
            lock_access!
            return :locked
          else
            save(:validate => false)
          end
        end

        result
      end

      protected

        def attempts_exceeded?
          self.failed_attempts > self.class.maximum_attempts
        end

        # Generates unlock token
        def generate_unlock_token
          self.unlock_token = self.class.unlock_token
        end

        # Tells if the lock is expired if :time unlock strategy is active
        def lock_expired?
          if unlock_strategy_enabled?(:time)
            locked_at && locked_at < self.class.unlock_in.ago
          else
            false
          end
        end

        # Checks whether the record is locked or not, yielding to the block
        # if it's locked, otherwise adds an error to email.
        def if_access_locked
          if access_locked?
            yield
          else
            self.errors.add(:email, :not_locked)
            false
          end
        end

      module ClassMethods
        # Attempt to find a user by it's email. If a record is found, send new
        # unlock instructions to it. If not user is found, returns a new user
        # with an email not found error.
        # Options must contain the user email
        def send_unlock_instructions(attributes={})
         lockable = find_or_initialize_with_errors(unlock_keys, attributes, :not_found)
         lockable.resend_unlock_token if lockable.persisted?
         lockable
        end

        # Find a user by it's unlock token and try to unlock it.
        # If no user is found, returns a new user with an error.
        # If the user is not locked, creates an error for the user
        # Options must have the unlock_token
        def unlock_access_by_token(unlock_token)
          lockable = find_or_initialize_with_error_by(:unlock_token, unlock_token)
          lockable.unlock_access! if lockable.persisted?
          lockable
        end

        # Is the unlock enabled for the given unlock strategy?
        def unlock_strategy_enabled?(strategy)
          [:both, strategy].include?(self.unlock_strategy)
        end

        # Is the lock enabled for the given lock strategy?
        def lock_strategy_enabled?(strategy)
          self.lock_strategy == strategy
        end

        def unlock_token
          Devise.friendly_token
        end

        Devise::Models.config(self, :maximum_attempts, :lock_strategy, :unlock_strategy, :unlock_in, :unlock_keys)
      end
    end
  end
end

If anyone can help guide me through setting this up, I'd appreciate your assistance. Sorry if the question is redundant or not clear.

Thanks in advance for your help.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

傲娇萝莉攻 2025-01-16 09:40:58

我的问题现已通过将 :locked_at, :failed_attempts 添加到 user.rb 中的 attr_accessible 得到解决

attr_accessible :account_name, :account_id, :email, :username, :password, :password_confirmation, :loginable_token, [...] :locked_at, :failed_attempts

My issue has now been solved by added :locked_at, :failed_attempts to attr_accessiblein user.rb

attr_accessible :account_name, :account_id, :email, :username, :password, :password_confirmation, :loginable_token, [...] :locked_at, :failed_attempts
愚人国度 2025-01-16 09:40:58

您是否尝试将可锁定移动到前面:

  devise :lockable,:invitable, :rememberable, :database_authenticatable, 
         :registerable,
         :recoverable, :trackable, :validatable,
         :authentication_keys => [:login], :reset_password_keys => [:login],
         :cookie_domain => :all
         

Did you try moving lockable to the front:

  devise :lockable,:invitable, :rememberable, :database_authenticatable, 
         :registerable,
         :recoverable, :trackable, :validatable,
         :authentication_keys => [:login], :reset_password_keys => [:login],
         :cookie_domain => :all
         
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文