免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

RailsAdmin 自定义授权完全指南:从 `authorize_with` 到自定义 AuthorizationAdapter 扩展

RailsAdmin 自定义授权完全指南:从 `authorize_with` 到自定义 AuthorizationAdapter 扩展 后端【免费下载链接】rails_adminRailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data项目地址https://gitcode.com/gh_mirrors/ra/rails_admin点击查看免费下载本篇技术指南围绕 RailsAdmin 的授权定制展开面向需要为后台管理界面接入自定义权限体系如基于角色、用户资料控制模型与操作访问的 Rails 开发者。文章以仓库官方文档 docs/customized-authorization.md 为核心骨架结合 RailsAdmin 源码config.rb、application_controller.rb与内置授权适配器实现cancancan、pundit进行纵深解读。读完你将掌握两种实战方案一是通过authorize_with块快速接入自定义登录用户校验二是通过实现AuthorizationAdapter接口编写完整的自定义授权扩展实现按模型、按操作、按记录范围的精细化权限控制。一、理解 RailsAdmin 的授权机制在动手定制之前需要先厘清 RailsAdmin 中授权Authorization所处的位置与触发时机。认证Authentication确认你是谁由authenticate_with配置默认行为是一个空 procDEFAULT_AUTHENTICATION proc {}见 config.rb。授权Authorization确认你能做什么由authorize_with配置默认同样为空 procDEFAULT_AUTHORIZE proc {}见 config.rb即默认不做任何授权限制。从 app/controllers/rails_admin/application_controller.rb 的源码可以看到每个 RailsAdmin 控制器动作都会依次执行三个 before_actionbefore_action :_authenticate! before_action :_authorize! before_action :_audit!其中_authorize!的实现就是对授权配置的即时求值L58-L60def _authorize! instance_eval(RailsAdmin::Config.authorize_with) end也就是说无论你选择哪种授权方案最终都会在这个控制器实例上下文中被instance_eval执行因此配置块内可以直接访问控制器方法如current_user、redirect_to。二、方式一使用authorize_with块进行自定义授权这是最轻量、最直接的接入方式。官方文档给出的完整示例位于config/initializer/rails_admin.rb中# in config/initializer/rails_admin.rb RailsAdmin.config do |config| config.authorize_with do |controller| redirect_to main_app.root_path unless current_user.try(:admin?) end end关键点说明块内既可以接收块变量controller也可以通过self访问当前控制器实例二者等价redirect_to main_app.root_path使用main_app.前缀调用主应用的root_path路由辅助方法——这是 Rails 引擎内调用宿主应用路由的标准写法当校验不通过时直接重定向离开后台而不是抛出异常current_user.try(:admin?)是常见的当前用户是否管理员判断实际应替换为你项目中的用户模型与权限判定逻辑该块在每个控制器动作前执行对应上文_authorize!before_action因此可实现整站后台的一刀切访问控制。对应的配置 API 定义见 lib/rails_admin/config.rb#L157-L169当传入块时authorize block当传入适配器名称时则创建对应适配器实例。二者的返回值统一由authorize || DEFAULT_AUTHORIZE兜底。三、关于parent_controller的关键说明官方文档特别指出如果正在做自定义授权或者你所用的授权库的current_user方法在 initializer 上下文中不可用请使用如下配置config.parent_controller ::ApplicationController为什么需要这一步从 application_controller.rb#L15 可以看到class ApplicationController Config.parent_controller.constantizeRailsAdmin 引擎的控制器默认继承自Config.parent_controller。其默认值为::ActionController::Base该默认值在 config_spec.rb 中有测试断言。而current_user这类方法通常定义在你的应用自己的ApplicationController中或由 Devise 等认证库注入。只有将 RailsAdmin 的父控制器改为宿主应用的ApplicationControllercurrent_user等应用级 helper 方法才能在授权块中被直接调用。补充两点你也可以显式配置当前用户方法config.current_user_method支持自定义_current_user的取值逻辑默认读取_request.env[warden].user详见 config.rb#L188-L199在适配器模式下官方适配器统一通过delegate :_current_user, to: :controller获取当前用户_current_user正是由 application_controller.rb#L44-L46 中instance_eval(RailsAdmin::Config.current_user_method)计算而来。四、方式二实现自定义 AuthorizationAdapter 扩展当授权规则足够复杂例如用户档案对不同模型、不同操作拥有差异化的访问权限还需要对查询结果集进行行级过滤时authorize_with块就不够用了。此时应当实现一个完整的授权扩展。官方文档将其定位为可以像已有的扩展一样实现的自定义扩展已有的扩展位于 lib/rails_admin/extensions包括 CancanCan、Pundit 与 PaperTrail审计等。4.1 注册扩展自定义扩展定义完成后需要通过两个步骤接入 RailsAdminRailsAdmin.add_extension(:my_custom_extension, RailsAdmin::Extensions::MyCustomExtension, authorization: true) RailsAdmin.config do |config| config.authorize_with :my_custom_extension endadd_extension的底层实现在 lib/rails_admin/extension.rb#L15-L25它把扩展注册进EXTENSIONS数组并根据 options 中的:authorization、:configuration、:auditing标记将对应模块常量写入AUTHORIZATION_ADAPTERS、CONFIGURATION_ADAPTERS、AUDITING_ADAPTERS哈希。随后config.authorize_with :my_custom_extension会在 config.rb#L157-L169 中查表RailsAdmin::AUTHORIZATION_ADAPTERS[extension]找到适配器类并在每次请求时实例化。作为对照内置扩展的注册方式完全相同例如 lib/rails_admin/extensions/cancancan.rb 中只有一行RailsAdmin.add_extension(:cancancan, RailsAdmin::Extensions::CanCanCan, authorization: true)Pundit 扩展同理见 pundit.rb。4.2 AuthorizationAdapter 接口契约官方文档明确适配器需要实现以下核心方法这也是 CancanCan/Pundit 两个内置适配器共同遵守的接口见 cancancan/authorization_adapter.rb 与 pundit/authorization_adapter.rb方法调用时机语义initialize(controller)每个请求创建适配器时保存控制器引用后续通过_current_user取当前用户authorize(action, abstract_model nil, model_object nil)每个控制器动作授权失败时抛异常成功则静默通过authorized?(action, abstract_model nil, model_object nil)主要从视图调用返回布尔值决定按钮/链接是否渲染query(action, abstract_model)list 与 bulk_delete/destroy 动作返回作用域scope限制用户可见/可操作的记录行attributes_for(action, abstract_model)new/create 动作返回新记录允许预填的属性哈希各方法在源码中的实际调用点印证如下authorized?用于视图中控制动作按钮的显隐config/actions/base.rb#L59authorize在 edit.rb#L30authorize(:update, ...)与 new.rb#L43authorize(:create, ...)等动作中调用query在 dashboard.rb#L32 中用于给列表查询挂上授权作用域attributes_for在 new.rb#L23 中用于初始化新记录的默认属性。4.3 官方文档完整示例逐段解读以下是官方文档提供的自定义扩展完整代码建议原样保留在lib/rails_admin/extensions/下你自己的命名空间中module RailsAdmin module Extensions module MyCustomExtension class AuthorizationAdapter attr_reader :controller delegate :_current_user, to: :controller SUPPORT_FULL_MODELS { Integration { except: %i[edit new export] } }.freeze def initialize(controller) controller controller end # This method is called in every controller action and should raise an # exception when the authorization fails. The first argument is the name # of the controller action as a symbol (:create, :bulk_delete, etc.). # The second argument is the AbstractModel instance that applies. The # third argument is the actual model instance if it is available. def authorize(action, abstract_model nil, model_object nil) return if authorized?(action, abstract_model, model_object) raise ActionController::RoutingError, Not Found end # This method is called primarily from the view to determine whether the # given user has access to perform the action on a given model. It # should return true when authorized. This takes the same arguments as # authorize. The difference is that this will return a boolean whereas # authorize will raise an exception when not authorized. def authorized?(action, abstract_model nil, model_object nil) # Devs can access everything return true if _current_user.has_access?(:support_dev) record model_object.model_name.name || abstract_model.model_name _current_user.has_access?(:support_full) authorized_for_full_support?(action, record) end # This is called when needing to scope a database query. It is called # within the list and bulk_delete/destroy actions and should return a # scope which limits the records to those which the user can perform the # given action on. def query(_action, abstract_model) # No query restrictions for now abstract_model.model.all end # This is called in the new/create actions to determine the initial # attributes for new records. It should return a hash of attributes # which match what the user is authorized to create. def attributes_for(_action, _abstract_model) # No attribute restrictions for now {} end private def authorized_for_full_support?(action, record) !record || SUPPORT_FULL_MODELS[record].fetch(:except, []).exclude?(action) end end end end end RailsAdmin.add_extension(:my_custom_extension, RailsAdmin::Extensions::MyCustomExtension, authorization: true) RailsAdmin.config do |config| config.authorize_with :my_custom_extension end逐段要点解析delegate :_current_user, to: :controller将_current_user委托给控制器。官方适配器如 CancanCan 的current_ability实现同样依赖_current_user而非current_user这正是上一节parent_controller配置之所以重要的原因。SUPPORT_FULL_MODELS声明完全支持full support用户组对Integration模型默认开放全部操作、但排除edit/new/export三个动作。except数组中的符号就是控制器动作名:create、:bulk_delete等。authorizevsauthorized?authorize内部复用authorized?的判定失败时抛出ActionController::RoutingError, Not Found。选择 404 而非 403 是一种常见的后台安全实践——避免向未授权用户暴露资源存在性信息。record的来源model_object.model_name.name || abstract_model.model_name优先使用具体模型实例的类名其次使用AbstractModel的模型名——这正是AbstractModel抽象层存在的意义它让同一套授权逻辑同时适用于 ActiveRecord 与 Mongoid见 lib/rails_admin/abstract_model.rb。query与attributes_for目前返回空实现分别返回全部记录abstract_model.model.all与空属性哈希实际项目中可按角色返回where作用域和默认属性。五、与内置适配器实现对照理解内置适配器如何实现同一接口有助于把握自定义扩展的设计边界。CancanCan 适配器authorization_adapter.rb的显著特点通过ControllerExtension模块注入current_ability方法内部使用ability_class.new(_current_user)构造 Ability 对象ability_class是一个可配置的注册选项默认值为Ability可通过config.authorize_with :cancancan, ability_class_name等形式定制resolve_action_and_subject决定授权主体优先用model_object || abstract_model.model两者皆无时如 dashboard回退为[:read, action]query委托给 CanCanCan 的accessible_byattributes_for委托给attributes_for说明行级过滤与字段级默认值完全可以委托给成熟的授权库。Pundit 适配器authorization_adapter.rb的显著特点action_for_pundit将 RailsAdmin 的动作名如bulk_delete转换为 Pundit policy 方法名追加?后缀如bulk_delete?authorize失败时抛出Pundit::NotAuthorizedErrorquery调用policy_scope且rescue Pundit::NotDefinedError回退为abstract_model.model.all即未定义 policy 时不做行级限制attributes_for通过policy(record).try(:attributes_for, action) || {}兼容未实现该方法的 policy。这两份实现就是自定义扩展的最佳范本——你的MyCustomExtension只需保证对authorize/authorized?/query/attributes_for四个方法作出符合语义的响应即可被 RailsAdmin 无缝驱动。六、实战注意事项与常见坑initializer 中current_user不可用自定义授权块或适配器依赖current_user时务必设置config.parent_controller ::ApplicationController否则current_user在引擎控制器上下文中不存在参见第三节。授权失败的表现形式authorize应当抛异常官方示例为 404 RoutingErrorCancanCan 抛CanCan::AccessDeniedPundit 抛NotAuthorizedError而authorized?只返回布尔值二者语义不可混用——前者面向动作执行后者面向视图渲染。query只对 list/bulk_delete 生效官方注释明确query仅在 list 与 bulk_delete/destroy 动作中被调用若需要对 show/edit 也做行级限制需要在authorize中自行结合model_object判断。动作名是符号authorize第一参数是控制器动作名:create、:bulk_delete、:new、:edit、:export等对应配置层的authorization_key参见 config/actions/base.rb。同一套接口适配不同 ORM因为abstract_model抽象层的存在自定义适配器无需关心底层是 ActiveRecord 还是 Mongoidabstract_model.model与model_name均已被统一可对照 lib/rails_admin/adapters/active_record.rb 与 lib/rails_admin/adapters/mongoid.rb。七、进一步阅读通用授权接入指南docs/authorization.mdCancanCan 集成docs/cancancan.mdPundit 集成docs/pundit.md基础访问认证Basic Access Authenticationdocs/basic-access-authentication.mdDevise 认证集成docs/devise.md授权适配器参考实现lib/rails_admin/extensions/cancancan/authorization_adapter.rb、lib/rails_admin/extensions/pundit/authorization_adapter.rb授权配置 API 与默认值lib/rails_admin/config.rb授权在控制器中的触发点app/controllers/rails_admin/application_controller.rb综上RailsAdmin 的授权体系设计得相当克制默认无授权、authorize_with支持块与适配器两种形态、适配器接口仅四个方法。无论是几分钟接入的authorize_with块还是面向复杂权限模型的完整AuthorizationAdapter扩展都在这套统一机制下运转——理解_authorize!→instance_eval→ 适配器方法分发的调用链就能在任意复杂度下安全、可控地定制你的后台权限体系。赞分享后端【免费下载链接】rails_adminRailsAdmin is a Rails engine that provides an easy-to-use interface for managing your data项目地址https://gitcode.com/gh_mirrors/ra/rails_admin点击查看免费下载相关推荐Spring Authorization Server自定义授权类型实现扩展授权流程的完整指南Spring Authorization Server自定义授权类型实现扩展授权流程的完整指南 Spring Authorization Server 是 S后端认证鉴权扩展 MXNet从自定义 Gluon 层到 Numpy 自定义算子的完整实战指南扩展 MXNet从自定义 Gluon 层到 Numpy 自定义算子的完整实战指南 导读 深度学习框架的扩展能力决定了它在真实业务中的边界。Apache MXN深度学习机器学习人工智能MaaFramework插件开发指南如何扩展自定义功能模块MaaFramework插件开发指南如何扩展自定义功能模块 MaaFramework是一个基于图像识别的自动化黑盒测试框架其强大的插件系统允许开发者轻松扩展测试计算机视觉上一篇InsightFace Server 维护者指南源码架构、严格推理契约与发布流程解析下一篇Pure Live 上游同步审查策略全解以 merge base 三方比较与语义台账守护维护分支创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表