【问题标题】:Does Rails come with a "not authorized" exception?Rails 是否带有“未授权”异常?
【发布时间】:2014-09-17 13:38:38
【问题描述】:

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

目前,我有一个名为NotAuthorized 的自定义异常类,我在控制器级别使用rescue_from,但我很想知道:Rails 4 是否已经带有一个异常来指示某个操作不是授权?我是否通过实施此例外重新发明轮子?

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

【问题讨论】:

  • 那么 http 错误代码 401 表示未经授权,您可以告诉 rails 返回 401 状态代码,并呈现您想要的任何视图
  • @MohammadAbuShady - 我相信他正在寻找像 raise ActionController::RoutingError.new('Not Found') 这样的东西,它会自动强制应用程序在没有任何救援状态的情况下呈现 404。
  • 我通常只使用 devise + cancan,一个很好的身份验证和授权组合
  • 同意@MohammadAbuShady,但是,如果你走那条路,请确保它是 CanCanCan,因为 CanCan 已被放弃 :)!
  • 我不知道,我去看看,谢谢你的信息

标签: ruby-on-rails ruby actioncontroller


【解决方案1】:

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

【讨论】:

  • 感谢您回答我的问题。我很好奇异常是如何用 Mongoid 作为 ORM 映射的。
  • @Stefan 的链接引用了 master 中的特定行号,该行号随时间而变化。这是该代码的更多“永久”链接:github.com/mongoid/mongoid/blob/v4.0.2/lib/mongoid/…
【解决方案2】:

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

通常这些是其他库Devise 对未认证的职责; PunditDude PolicyCanCanCan,Rollify for NotAuthorized)我实际上认为扩展 ActionController 可能是一件坏事p>

所以我通常解决这个问题的方式是我在 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

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

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

您可以阅读很多文章,其中人们对 Rails raise - rescue_from 是现代的 goto(现在正在考虑一些专家中的反模式)保持警惕,并且在某种程度上这是正确的,但前提是您正在拯救您无法完全控制的异常!

这意味着第 3 方例外(包括 Devise 和 Rails 到某一点)。如果您在应用程序中定义异常类,则您不会在第 3 方库上进行中继 => 您拥有完全控制权 => 您可以 rescue_from 而这不是反模式。

【讨论】:

    猜你喜欢
    • 2015-12-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-03-11
    • 2012-02-22
    • 2014-07-01
    • 1970-01-01
    相关资源
    最近更新 更多