【问题标题】:Allow unconfirmed users to access certain pages which require authentication允许未经确认的用户访问某些需要身份验证的页面
【发布时间】:2018-08-07 10:31:05
【问题描述】:

我将 Rails 堆栈用于

  • 设计
  • 典狱长
  • 已确认

现在我有一些与电子邮件确认和未验证用户访问权限相关的要求。假设有 3 类页面:

  1. case 1 - 不需要身份验证。
  2. case 2 - 需要身份验证,还需要确认用户。
  3. case 3 - 需要身份验证(正确的用户名和密码组合),但无需确认用户即可访问它们。

通过设计和确认,实现case 1case 2 轻而易举。用户登录/注册后,我会重定向到“确认您的电子邮件页面”。

我的问题是case 3。假设用户进行他的登录/注册。他尚未确认他的电子邮件地址,但应该被允许访问某些case 3 路线。当他访问案例 3 路线时,我期望:

  • 无重定向
  • 有效会话

可确认的设计要么允许已确认的用户访问所有页面,要么不允许访问所有页面。它不允许通过身份验证但未经确认访问某些页面。

我尝试通过实现这个逻辑来覆盖设计confirmed?

class ApplicationController < ActionController::Base
   before_filter :verify_user
   def verify_user
       $flag = true if CERTAIN_ROUTES.include?(action_class)
   end
end

class User < ActiveRecord::Base
   def confirmed?
      $flag || !!confirmed_at
   end
end

这几乎不适用于登录,但不适用于注册。此外,这是实现它的一种非常糟糕的方法。我应该如何解决这个问题?其他功能正常。

【问题讨论】:

    标签: ruby-on-rails ruby-on-rails-3 devise warden devise-confirmable


    【解决方案1】:

    您可以只覆盖confirmation_required? 模型方法(docs),而不是覆盖confirmed?

    # user.rb
    class User < ActiveRecord::Base
      protected
    
      def confirmation_required?
        false
      end
    end
    

    之后,您可以自己处理确认逻辑,当控制器需要确认时,通过在 before_action 中重定向所有未确认的用户,或者您可以将其拉入您的授权逻辑(例如使用权威人士)。

    class ApplicationController < ActionController::Base
       def needs_confirmation
         redirect_to root_path unless current_user.confirmed?
       end
    end
    
    class SomeController < ApplicationController
      before_action :needs_confirmation
    end
    

    【讨论】:

    • 非常感谢您的回答。但这是问题所在。我可能有 50 条路线,其中我需要 4 条路线才能通过身份验证但未经确认访问。我必须在每个控制器中写 before_action :needs_confirmation 并确保编写新控制器的任何其他开发人员添加此过滤器。你能推荐任何其他巧妙的解决方案吗?
    • 然后只需将“before_action”放在ApplicationController 中,并在您想要未经确认的访问的控制器中使用skip_before_action :needs_confirmationapidock.com/rails/v4.2.7/AbstractController/Callbacks/…
    • 这看起来是一个更好的解决方案。让我试一试,然后再回复你。需要 24 小时。
    • 非常感谢。这个解决方案非常适合我。
    【解决方案2】:

    您应该看看 gem 'pundit' - 它与设计配合得很好。

    https://github.com/varvet/pundit

    您无需编写控制器 before_actions 等,而是编写涵盖每个授权要求的策略,然后在控制器内部使用这些策略。

    例如,在控制器中:

    class ExampleController < ApplicationController
        before_action { authorize :example }
    
        def case_one
           # action
        end
    
        def case_two
           # action
        end
    
        def case_three
           # action
        end
    end
    

    那么您的策略将保存在 app/policies/example_policy.rb 下

    class ExamplePolicy < ApplicationPolicy
      attr_reader :user
    
      def initialize(user, _)
        @user = user
      end
    
      def case_one?
        true
      end
    
      def case_two?
        user.present? && user.confirmed_at.present?
      end
    
      def case_three?
        user.present?
      end
    end
    

    它工作得非常好,尤其是在您确定对某种资源的授权的其他情况下。

    【讨论】:

    • 如果没有 Confirmed_at,我如何将用户重定向到确认页面(他们可以在其中请求新的确认电子邮件)?
    • 您可以抢救Pundit::NotAuthorizedError,然后将用户重定向到您的确认页面。详情见github.com/varvet/…
    • 好的,但是如果我有 50 个控制器并且我只想将其中 3 个控制器的访问权限授予不符合我的授权要求的用户怎么办?我必须在 47 个控制器中的每一个和每个新创建的控制器中放置授权(假设所有人可用的控制器数量没有改变)。我是对的还是我错过了什么?
    • mr_muscle - 不,你不会那样做。您可以更改您的ApplicationController 以实现授权before_action,然后在您不希望它运行的控制器中执行操作之前跳过该操作。这只是一种方法,根据您的应用,您可能希望创建一个 AuthenticatedApplicationController 来继承等。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-07-08
    • 2020-03-19
    • 2021-07-04
    • 2019-08-19
    • 2019-07-13
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多