You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

ApplicationController未捕获Pundit Not Authorized Error问题排查

非管理员访问Rails Admin时Pundit异常未被捕获的问题解决

问题背景

你在使用Rails 5.2、Pundit 1.1和Rails Admin 1.2开发项目时,已经在application_controller.rb中配置了捕获Pundit::NotAuthorizedError并跳转首页提示,但非管理员用户访问Rails Admin仪表盘时,这个异常却没有被触发——预期的跳转和授权提示完全没出现。

你的相关代码如下:

application_controller.rb

class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include Pundit
  rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized
  private
  def user_not_authorized(exception)
    flash[:alert] = "You are not authorized to perform this action."
    redirect_to root_path
  end
end

application.policy.rb

class ApplicationPolicy
  ...
  def rails_admin?(action)
    case action
    when :dashboard, :index, :show, :new, :edit, :destroy, :export, :history, :show_in_app
      user.admin?
    else
      raise ::Pundit::NotDefinedError, "unable to find policy #{action} for #{record}."
    end
  end
end

config/initializers/rails_admin.rb

RailsAdmin.config do |config|
  ### Popular gems integration
  ## == Devise ==
  config.authenticate_with do
    warden.authenticate! scope: :user
  end
  config.current_user_method(&:current_user)
  ## == Pundit ==
  config.authorize_with :pundit
end
module RailsAdmin
  module Extensions
    module Pundit
      class AuthorizationAdapter
        def authorize(action, abstract_model = nil, model_object = nil)
          record = model_object || abstract_model && abstract_model.model
          if action && !policy(record).send(*action_for_pundit(action))
            raise ::Pundit::NotAuthorizedError.new("not allowed to #{action} this #{record}")
          end
          @controller.instance_variable_set(:@_pundit_policy_authorized, true)
        end
        def authorized?(action, abstract_model = nil, model_object = nil)
          record = model_object || abstract_model && abstract_model.model
          policy(record).send(*action_for_pundit(action)) if action
        end
        def action_for_pundit(action)
          [:rails_admin?, action]
        end
      end
    end
  end
end

问题根源

这事儿的核心原因很直白:Rails Admin自带的控制器并没有继承你编写的ApplicationController,它默认继承的是RailsAdmin::ApplicationController。你在自己的控制器里设置的rescue_from是全局捕获,但只对继承它的业务控制器生效——而Rails Admin的控制器不在这个范围内,所以抛出的Pundit::NotAuthorizedError根本没被你的捕获逻辑接住。

两种修复方案

方案1:给Rails Admin控制器单独添加异常捕获

直接在config/initializers/rails_admin.rb中,给Rails Admin的控制器注入异常处理逻辑:

RailsAdmin.config do |config|
  # 保留你原有的配置
  config.authenticate_with do
    warden.authenticate! scope: :user
  end
  config.current_user_method(&:current_user)
  config.authorize_with :pundit

  # 新增:给Rails Admin控制器添加异常捕获
  config.controller do
    rescue_from Pundit::NotAuthorizedError, with: :user_not_authorized

    private

    def user_not_authorized(exception)
      flash[:alert] = "You are not authorized to perform this action."
      redirect_to root_path
    end
  end
end

这种方式不需要改动现有控制器结构,针对性解决当前问题。

方案2:让Rails Admin控制器继承你的ApplicationController

如果你希望Rails Admin复用ApplicationController里的所有公共逻辑(比如通用权限校验、日志处理、其他异常捕获等),可以直接指定Rails Admin的父控制器:

RailsAdmin.config do |config|
  # 保留原有配置...
  config.parent_controller = 'ApplicationController'
end

这样Rails Admin的所有控制器都会继承你的ApplicationController,自然就能用上里面的rescue_from了。不过要注意,这种方式需要确保你的ApplicationController里的逻辑不会和Rails Admin的内置功能冲突。

两种方案都能实现你想要的效果:非管理员用户访问Rails Admin时,会被跳转到首页并显示授权提示。

内容的提问来源于stack exchange,提问作者chell

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.15 08:49:25