Ruby on Rails 4:使用多个Devise模型进行身份验证后的授权

时间:2014-02-03 18:26:46

标签: authentication devise ruby-on-rails-4 authorization multiple-models

我创建了一个简单的Ruby on Rails 4应用程序,其中包含一些简单模型和部署在我的测试VPS上的PosgreSQL数据库。然后我使用Rails生成器创建了三个Devise模型。

我选择了单独的Devise模型,因为模型彼此完全不同。 single table inheritance方法毫无疑问。

据我所知,对于任何Devise模型,我都可以在网站上执行身份验证,注册“设计”用户等。这些东西可行。

现在我计划建立一个地方来进行授权。我放弃了CanCan,因为根据我在Google上发现的内容,目前Rails 4不支持。

所以我找到的最合适的选项只是使用before_filter和自定义身份验证方法,后者又会检查current_user类型或其存在,并返回它是否合适。

以下是我尝试过的示例伪代码,它看起来很有效。

before_filter :custom_authentication!

def custom_authentication!
  if current_admin
    redirect_to "admin_page"
  elsif current_monkey
    redirect_to "zoo"
  elsif current_human
    redirect_to "home"
  else
    # some unauthorised access alert
  end
end

据我了解,我需要将这种代码放入我的Rails 4应用程序中的每个控制器中。这是对的吗?

我的主要问题是:有更好的方法吗?我想在application_controller.rb中放置一个方法,为每个访问我的app控制器的Devise用户模型构建整个授权逻辑。对于这种逻辑,这会是一个更好的地方吗?这是正确的方法吗?或者我是否需要重新启动并使用其他方法?

2 个答案:

答案 0 :(得分:1)

我在Rails 4中发现CanCan不支持后试图找到用户授权解决方案时遇到了同样的问题。我选择使用的选项是Pundit。它非常灵活,效果很好。

https://github.com/elabs/pundit

基本上,您设置了一组策略类来管理控制器操作的用户授权。

posts_controller的策略类与此类似:

class PostPolicy
  attr_reader :user, :post

  def initialize(user, post)
    @user = user
    @post = post
  end

  def update?
    user.admin?
  end
end

您将在控制器中授权更新操作:

def update
  @post = Post.find(params[:id])
  authorize @post
  if @post.update(post_params)
    redirect_to @post
  else
    render :edit
  end
end

因此,如果用户不是管理员,则会引发错误。 Pundit允许您在ApplicationController中挽救被拒绝的授权,并允许您添加自定义代码。例如,你可以这样做:

class ApplicationController < ActionController::Base
  protect_from_forgery
  include Pundit

  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

private

  def user_not_authorized
    if current_monkey
      redirect_to "zoo"
    elsif current_human
      redirect_to "home"
    else
      flash[:error] = "You are not authorized to perform this action."
      redirect_to request.headers["Referer"] || root_path
    end
  end
end

您还可以在同一Policy类中为多个设计用户配置策略。例如,在PostPolicy类中,您可以检查用户的类型并根据该权限授予权限:

def index?
  return true if user.type == "Monkey"
end

答案 1 :(得分:0)

像miahabdu所说,首先是身份验证(看用户是否存在),然后是授权(请参阅用户的权限)。

但是,您不必停止使用设计,也不需要Cancan。

以下是执行“授权”的简单方法。在认证之后。

class ApplicationController < ActionController::Base
  # Prevent CSRF attacks by raising an exception.
  # For APIs, you may want to use :null_session instead.
  protect_from_forgery with: :exception
  before_action :authenticate_user!
  before_action :check_access

  private

  def check_access
    if current_user.present?
      unless find_permission(current_user, 'code assigned for login access')
        sign_out current_user
        redirect_to new_user_session_path
      end
    end

  end

end

不要过于担心find_permission。基本上current_user来自设计,sign_out current_user就是这样。但是,在您签署current_user之后,您需要redirect_to new_user_session_path,假设User模型是您用于设计的模型。

现在,我在代码中所做的唯一一个小改动是使用CaseElse而不是IfElse。

因此,要将代码实现到我的示例中,我会对check_access方法执行此操作:

def check_access
  if current_user.present?
    unless find_permission(current_user, 'code assigned for login access')
      sign_out current_user
      redirect_to new_user_session_path
    end

    if current_admin
      redirect_to "admin_page"

    elsif current_monkey
      redirect_to "zoo"

    elsif current_human
      redirect_to "home"

    else
      sign_out current_user
      redirect_to new_user_session_path

    end

  end

end

最后,为什么要在开始时检查注销权限?它更快。您不必花费系统时间检查所有权限,但最终还是不允许用户登录。

  • 首先,用户存在吗? - 身份验证
  • 第二,用户是否登录? - 授权
  • 第三,权限是什么? - 授权