1

我已将所有用户身份验证代码放在一个位置,即 lib/auth.rb。它看起来像这样:

lib/auth.rb

module Admin

  def do_i_have_permission_to?(permission)
    # Code to check all of this goes here
  end

end

我将此模块作为应用程序助手的一部分包含在内,因此这些功能在所有视图中都可用:

application_helper.rb

require 'auth'
module ApplicationHelper
  include Admin
  # other stuff here
end

而且我还将它作为应用程序控制器的一部分包含在内,因此控制器同样可以调用函数:

应用程序.rb

require 'auth'
class ApplicationController < ActionController::Base
  include Admin
end

到现在为止还挺好。

问题是我的应用程序不像普通的网络应用程序。具体来说,多个用户可以同时从同一台计算机登录系统(使用相同的浏览器)。我通过查看从该 IP 登录的所有人员来对操作进行身份验证,如果他们都能做到,则通过。

这意味着,如果管理员想要做某事,该管理员必须先将其他所有人注销,这很烦人。但是我们希望管理员所做的一切都得到管理员的批准。所以给我的建议是拥有它,以便管理员可以在他们通常无法访问的任何页面上提供用户名/密码组合(例如,“编辑用户”页面将具有这些额外的输入字段)并且身份验证例程将检查那个。这表示

Admin::do_i_have_permission_to?(permission)

需要获取当前请求参数。我不能像在控制器中那样只使用 params[:foo],因为 params 没有定义;同样 request.parameters[:foo] 也不起作用。我的搜索显示:

  • 当前搜索参数在当前请求中,
  • 当前请求在当前控制器中,
  • 当前控制器在当前调度器中,并且
  • 我不确定当前的调度程序是否保存在任何地方。

也就是说,经验告诉我,当我跳过这么多圈时,我很可能做错了。那么正确的方法是什么?我考虑过的选项是:

  • 只需将当前在 auth.rb 中的所有功能移动到 ApplicationHelper 中(我认为)他们将可以访问请求等。有效,但把帮手弄得一团糟。
  • 将所有功能移到其他地方,他们会看到这些方法(我不知道在哪里)
  • 我只是缺少一些东西。
4

1 回答 1

4

在典型的 Rails 应用程序中,身份验证信息存储在活动会话中,而不是参数中。因此,编写一个可以满足您需求的助手非常简单。

创建一个然后包含在 ApplicationHelper 中的模块似乎相当不正统。传统的方法是创建一个单独的帮助程序,在这种情况下可能称为 AuthenticationHelper。然后可以将其包含在任何所需的控制器中,或者如果您愿意,可以将其加载到 ApplicationController 中以使其普遍可用。

一般而言,Helper 不应包含其他 Helpers。最好将多个帮助程序简单地加载到给定的控制器中。

辅助方法可以完全访问在它们操作的控制器上下文中声明的任何实例变量。具体来说,这些只是实例变量(@name),而不是局部变量(name)。辅助方法也会针对特定视图执行。

此外,我不确定为什么用户会在同一步骤中提供凭据并执行操作,至少对于传统的基于 Web 的应用程序而言。通常该过程是登录然后单独执行操作。

但是,对于每个事务都是独立操作的 API,最直接的方法是提取处理身份验证的相关请求参数,建立一些控制器实例变量,然后继续执行给定的特定请求凭证施加的约束。

对于这类事情,我通常采用的方法是在 ApplicationController 本身中添加一个身份验证结构,该结构可以执行所需的检查。这些是受保护的方法。

虽然很容易将它们卷入一大堆,例如 can_edit_user?和can_create_group?这些很快就会失控。为通用的 can_perform 挂上钩子是一种更简单的设计吗?或 has_authority_to?传递操作和任何必需参数的方法。

例如,一个非常粗略的实现:

  class ApplicationController < ActionController::Base
  protected
    def has_authority_to?(operation, conditions = { })
      AuthenticationCheck.send(operation, conditions)
    rescue
      false
    end
  end

  module AuthenticationCheck
    def self.edit_user?(conditions)
      session_user == conditions[:user]
    end
  end

  class UserController
    # ...

    def edit
      @user = User.find(params[:id])

      unless (has_authority_to?(:edit_user, :user => @user))
        render(:partial => 'common/access_denied', :status => :forbidden)
      end
    rescue ActiveRecord::RecordNotFound
      render(:partial => 'users/not_found')
    end
  end

显然,您希望将大量权限检查滚动到 before_filter 块中,以避免重复并提高一致性。

一个完整的框架示例可能会更有帮助,例如 Wristband 用户身份验证系统:

http://github.com/theworkinggroup/wristband/tree/master

于 2009-06-16T16:32:49.197 回答