Rails 是否有“未经授权”的例外情况?

问题描述 投票:0回答:2

我正在编写一个应用程序,它使用普通的旧式 Ruby 对象 (PORO) 从控制器中抽象出授权逻辑。

目前,我有一个名为

NotAuthorized
的自定义异常类,我在控制器级别
rescue_from
,但我很想知道:Rails 4 是否已经带有一个异常来指示操作未经授权? 我我通过实现这个异常来重新发明轮子?

澄清

raise AuthorizationException
不会发生在控制器内部的任何地方,它发生在控制器外部完全解耦的 PORO 内部。该对象不了解 HTTP、路由或控制器。

ruby-on-rails ruby actioncontroller
2个回答
38
投票

Rails 似乎没有将异常映射到

:unauthorized

默认映射在 activerecord/lib/active_record/railtie.rb:

中定义
config.action_dispatch.rescue_responses.merge!(
  'ActiveRecord::RecordNotFound'   => :not_found,
  'ActiveRecord::StaleObjectError' => :conflict,
  'ActiveRecord::RecordInvalid'    => :unprocessable_entity,
  'ActiveRecord::RecordNotSaved'   => :unprocessable_entity
)

actionpack/lib/action_dispatch/middleware/exception_wrapper.rb

@@rescue_responses.merge!(
  'ActionController::RoutingError'             => :not_found,
  'AbstractController::ActionNotFound'         => :not_found,
  'ActionController::MethodNotAllowed'         => :method_not_allowed,
  'ActionController::UnknownHttpMethod'        => :method_not_allowed,
  'ActionController::NotImplemented'           => :not_implemented,
  'ActionController::UnknownFormat'            => :not_acceptable,
  'ActionController::InvalidAuthenticityToken' => :unprocessable_entity,
  'ActionDispatch::ParamsParser::ParseError'   => :bad_request,
  'ActionController::BadRequest'               => :bad_request,
  'ActionController::ParameterMissing'         => :bad_request
)

您可以从应用程序的配置中添加自定义异常(或自定义 Railtie):

Your::Application.configure do

  config.action_dispatch.rescue_responses.merge!(
    'AuthorizationException' => :unauthorized
  )

  # ...

end

或者直接使用

rescue_from


31
投票

我猜测 Rails 没有引入此异常的原因是因为授权和身份验证不是 Rails 本机行为(当然不考虑 basicauth)。

通常这些是其他库的职责 Devise for NotAuthenticated; PunditDude PolicyCanCanCan、Rollify for NotAuthorized)我实际上认为用

ActionController
等自定义异常扩展
ActionController::NotAuthorized
可能是一件坏事(因为就像我说的那样,这不是它的责任)

所以我通常解决这个问题的方式是在

ApplicationController

上引入自定义异常
class ApplicationController  < ActionController::Base
  NotAuthorized = Class.new(StandardError)
  # ...or if you really want it to be ActionController
  # NotAuthorized = Class.new(ActionController::RoutingError)

  rescue_from ActiveRecord::RecordNotFound do |exception|
    render_error_page(status: 404, text: 'Not found')
  end

  rescue_from ApplicationController::NotAuthorized do |exception|
    render_error_page(status: 403, text: 'Forbidden')
  end

  private

  def render_error_page(status:, text:, template: 'errors/routing')
    respond_to do |format|
      format.json { render json: {errors: [message: "#{status} #{text}"]}, status: status }
      format.html { render template: template, status: status, layout: false }
      format.any  { head status }
    end
  end
end

因此在我的控制器中我可以做

class MyStuff < ApplicationController
  def index
    if current_user.admin?
      # ....
    else 
      raise ApplicationController::NotAuthorized
    end
  end
end

这清楚地定义了您期望引发和捕获此异常的层是您的应用程序层,而不是第 3 方库。

问题是,库可以更改(是的,这也意味着 Rails),在第 3 方库类上定义异常并在应用程序层中拯救它们确实很危险,就好像异常类的含义发生变化一样,它会阻止您的

rescue_from

你可以读到很多文章,人们都在警告 Rails

raise
-
rescue_from
是现代的
goto
(现在考虑一些专家的反模式),在某种程度上这是正确的,但前提是你要拯救您无法完全控制的异常!!

这意味着第 3 方例外(在某种程度上包括 Devise 和 Rails)。如果您在应用程序中定义异常类,则您不会中继第 3 方库 => 您拥有完全控制权 => 您可以

rescue_from
而不会成为反模式。

© www.soinside.com 2019 - 2024. All rights reserved.