ARTICLE DETAIL

资讯详情

深耕编程入门与网站建设的一线实战洞察。

Active Admin 授权适配器(Authorization Adapter)实战指南:从自定义策略到 CanCanCan 与 Pundit 集成

Active Admin 授权适配器(Authorization Adapter)实战指南:从自定义策略到 CanCanCan 与 Pundit 集成 后端【免费下载链接】activeadminThe administration framework for Ruby on Rails applications.项目地址https://gitcode.com/gh_mirrors/ac/activeadmin点击查看免费下载导读Active Admin 是 Ruby on Rails 的管理后台框架其内置的授权体系允许开发者通过**授权适配器Authorization Adapter**接管当前用户能否执行某个动作的全部判断逻辑。本篇指南以 docs/13-authorization-adapter.md 为主体结合仓库源码与测试用例系统讲解如何编写自定义授权适配器、如何在控制器与视图中校验权限、以及如何开箱即用地接入 CanCanCan 与 Pundit 两套主流授权方案。读完本文你将能够在 Active Admin 中实现细粒度的资源/页面访问控制、集合级数据隔离并规避常见的重定向循环等集成陷阱。授权适配器的工作方式一切从#authorized?开始Active Admin 的授权体系建立在一个非常简单的契约之上在每次动作发生时调用适配器的#authorized?方法由其返回true/false决定是否放行。默认情况下内置的基类ActiveAdmin::AuthorizationAdapter对一切请求都返回true即默认不限制任何访问它同时是自定义适配器的唯一起点。从源码看基类的完整契约定义在 lib/active_admin/authorization_adapter.rbinitialize(resource, user)每个请求都会实例化一次适配器resource是用户当前所在页面对应的ActiveAdmin::Resource或ActiveAdmin::Pageuser则来自控制器的current_active_admin_user。authorized?(action, subject nil)核心判定方法默认恒返回true。scope_collection(collection, action Auth::READ)集合作用域钩子默认原样返回传入的集合供授权库如 CanCanCan对索引页的数据进行行级过滤。对应测试 spec/unit/authorization/authorization_adapter_spec.rb 明确断言了这两个默认行为默认放行与默认不做集合过滤。此外适配器还提供了一个私有的normalized(klass)工具方法见 lib/active_admin/authorization_adapter.rb用于解决case语句中subject 可能是类、也可能是实例的匹配难题——详见下文自定义适配器一节。编写并挂载自定义授权适配器第一步定义适配器类自定义适配器的标准姿势是继承ActiveAdmin::AuthorizationAdapter并覆写#authorized?。文档给出的经典示例是仅允许作者更新和删除自己的文章# app/models/only_authors_authorization.rb class OnlyAuthorsAuthorization ActiveAdmin::AuthorizationAdapter def authorized?(action, subject nil) case subject when normalized(Post) # Only let the author update and delete posts if action :update || action :destroy subject.author user else true end else true end end end这里有两个值得展开的细节normalized(Post)的作用#authorized?收到的subject既可能是模型实例也可能是模型类例如判断全局导航中是否显示某个资源时传入的就是资源类。normalized(Post)返回一个NormalizedMatcher对象其实现为klass other || other.is_a?(klass)见 lib/active_admin/authorization_adapter.rb从而让when normalized(Post)同时匹配Post类和Post实例。单测 spec/unit/authorization/authorization_adapter_spec.rb 对匹配类、匹配实例、不匹配无关类型四种情形均有覆盖。user从哪来适配器构造时被注入了当前用户直接调用#user即可。第二步在初始化器中挂载编辑应用根目录下的config/initializers/active_admin.rb加入或修改一行config.authorization_adapter OnlyAuthorsAuthorization配置值既可以是类名字符串也可以是类本身——控制器在真正使用时通过active_admin_authorization_adapter统一处理字符串会被constantize为类见 app/controllers/active_admin/base_controller/authorization.rb。挂载之后每一次控制器动作执行前都会调用该适配器的#authorized?。第三步按命名空间分别配置授权适配器可以按 Active Admin 命名空间粒度配置这在同一应用内不同后台使用不同权限策略的场景下非常有用ActiveAdmin.setup do |config| config.namespace :admin do |ns| ns.authorization_adapter AdminAuthorization end config.namespace :my do |ns| ns.authorization_adapter DashboardAuthorization end endauthorization_adapter是命名空间级可继承设置其默认值即基类ActiveAdmin::AuthorizationAdapter见 lib/active_admin/namespace_settings.rb。测试 spec/unit/namespace/authorization_spec.rb 验证了应用级默认值与命名空间级覆写两条路径都有效且可以分别设置。在适配器中获取当前用户适配器内通过#user方法即可拿到当前登录用户从而写出只有管理员放行这类最简单直接的策略class OnlyAdmins ActiveAdmin::AuthorizationAdapter def authorized?(action, subject nil) user.admin? end end注意user的内容取决于你在 Active Admin 初始化器中配置的current_user_method默认方法名为current_admin_user适配器只是接收控制器传入的当前用户本身不负责认证。集合级数据隔离覆写scope_collection除能不能执行动作外授权适配器还提供一个#scope_collection(collection, action Auth::READ)钩子用于对资源集合做行级过滤。文档给出的仅能访问本账号数据示例class OnlyMyAccount ActiveAdmin::AuthorizationAdapter def authorized?(action, subject nil) subject.account user.account end def scope_collection(collection, action Auth::READ) collection.where(account_id: user.account_id) end end索引页Index Screen上展示的所有集合都会先经过这个方法再查询数据库。从源码验证资源注释功能在渲染评论列表时同样会调用active_admin_authorization.scope_collection(...)对评论集合做作用域处理见 lib/active_admin/orm/active_record/comments/views/active_admin_comments.rb说明这一钩子在 Active Admin 内部被广泛用于统一过滤入口。action参数默认是Auth::READ见 lib/active_admin/authorization_adapter.rb即只有只读/索引操作会触发集合作用域。管理页面Page的访问控制自定义页面ActiveAdmin.register_page与资源一样会被授权校验。授权页面时subject是ActiveAdmin::Page实例可以通过#name与#namespace.name判断具体是哪个页面class OnlyDashboard ActiveAdmin::AuthorizationAdapter def authorized?(action, subject nil) case subject when ActiveAdmin::Page action :read subject.name Dashboard subject.namespace.name :admin else false end end endCucumber 特性测试 features/authorization.feature 展示了真实场景当subject.name No Access时返回false访问该页面会看到 You are not authorized to perform this action 的提示而 Dashboard 则正常放行。四种动作类型与常量Active Admin 将控制器动作归纳为 4 个语义化动作授权适配器收到的action参数通常是其中之一动作控制范围:read是否能看到菜单项、索引页与详情show页:create是否能看到新建页并提交表单执行创建:update是否能看到编辑页并提交表单执行更新:destroy是否允许删除资源这四个动作均以常量形式暴露在ActiveAdmin::Authorization模块下READ/NEW/CREATE/EDIT/UPDATE/DESTROY见 lib/active_admin/authorization_adapter.rb且ActiveAdmin::Auth是其别名。控制器层通过ACTIONS_DICTIONARY把 HTTP 动作映射到这些权限名——例如index/show映射为READnew映射为NEWcreate映射为CREATEedit映射为EDITupdate映射为UPDATEdestroy映射为DESTROY见 app/controllers/active_admin/base_controller/authorization.rb。无法识别的自定义动作名会原样透传例如后文自定义动作:publish会直接以:publish参与授权判断。在控制器和视图中校验授权Active Admin 把两个辅助方法暴露到控制器和所有视图通过helper_method声明见 app/controllers/active_admin/base_controller/authorization.rbauthorized?条件判断用于视图渲染在索引页的列中按权限决定是否渲染编辑链接ActiveAdmin.register Post do index do column :title column do |post| link_to Edit, admin_post_path(post) if authorized? :update, post end end end该方法的实现就是委托给当前命名空间的授权适配器见 app/controllers/active_admin/base_controller/authorization.rb。此外内置的ActionItem与菜单系统也在底层大量使用该检查来控制入口显隐例如仅在有权限时显示新建/编辑/删除操作按钮见 lib/active_admin/resource/action_items.rb与仅在有读取权限时显示菜单项见 lib/active_admin/resource/menu.rb。authorize!强制执行用于自定义动作如果是在自定义控制器动作里做权限校验使用#authorize!未通过时会抛出ActiveAdmin::AccessDenied异常见 app/controllers/active_admin/base_controller/authorization.rb。AccessDenied携带user、action、subject三个属性便于记录与排查见 lib/active_admin/error.rb。文档给出的发布文章示例完整呈现了校验 业务执行 操作入口的闭环ActiveAdmin.register Post do member_action :publish, method: :post do post Post.find(params[:id]) authorize! :publish, post post.publish! flash[:notice] Post has been published redirect_to [:admin, post] end action_item :publish, only: :show do if !post.published? authorized?(:publish, post) link_to Publish, publish_admin_post_path(post), method: :post end end end开箱即用的授权库集成CanCanCan 适配器直接子类化AuthorizationAdapter属于底层方案大多数场景下使用声明式的授权 DSL 更省力。Active Admin 为CanCanCan提供了内置适配器ActiveAdmin::CanCanAdapter实现见 lib/active_admin/cancan_adapter.rb。配置步骤在config/initializers/active_admin.rb中修改config.authorization_adapter ActiveAdmin::CanCanAdapter适配器将#authorized?委托给cancan_ability.can?(action, subject)并将#scope_collection委托给collection.accessible_by(cancan_ability, action)——这正是 CanCan 提供行级过滤的入口。Ability 类默认名为Ability该默认值由适配器注册的cancan_ability_class可继承设置决定见 lib/active_admin/cancan_adapter.rb可用配置项替换config.cancan_ability_class MyCustomAbility防止重定向循环on_unauthorized_access仅配置适配器还不够——当用户访问无权页面被拒绝时默认行为可能触发重定向循环官方 issue 曾记录此问题。Active Admin 提供了on_unauthorized_access配置指定一个在ApplicationController中定义的方法名config.on_unauthorized_access :access_deniedclass ApplicationController ActionController::Base protect_from_forgery def access_denied(exception) redirect_to admin_organizations_path, alert: exception.message end end该方法把用户从无权访问的页面重定向到其有权访问的资源页上例为 organizations同时在浏览器中展示错误信息。从源码看on_unauthorized_access的默认值是符号:rescue_active_admin_access_denied对应控制器内置的兜底处理器见 lib/active_admin/namespace_settings.rb 与 app/controllers/active_admin/base_controller/authorization.rb异常最终以ActiveAdmin::AccessDenied形式被rescue_from捕获并分发见 app/controllers/active_admin/base_controller/authorization.rb。声明式 Ability 示例# app/models/ability.rb class Ability include CanCan::Ability def initialize(user) can :manage, Post can :read, User can :manage, User, id: user.id can :read, ActiveAdmin::Page, name: Dashboard, namespace_name: admin end end注意最后一行的用法CanCan 适配器同样支持对ActiveAdmin::Page授权与自定义适配器中页面即 subject的约定一致。配置完成后CanCanCan 的声明规则即成为 Active Admin 的权限来源。开箱即用的授权库集成Pundit 适配器Active Admin 同样内置了Pundit适配器ActiveAdmin::PunditAdapter实现见 lib/active_admin/pundit_adapter.rb。配置步骤config.authorization_adapter ActiveAdmin::PunditAdapter配置完成后Active Admin 会自动读取你的 Pundit Policy 并用于授权。适配器内部对动作做了映射:read在 subject 为类时调用index?、为实例时调用show?:destroy在 subject 为类时调用destroy_all?供批量删除/批量动作使用、为实例时调用destroy?其余动作按#{action}?命名规则查找见 lib/active_admin/pundit_adapter.rb。它同时支持通过pundit_policy_namespace与pundit_default_policy两个可继承设置控制策略查找的命名空间与兜底策略见 lib/active_admin/pundit_adapter.rb相关行为在 spec/unit/pundit_adapter_spec.rb 中有系统覆盖。与 Pundit 强制校验的冲突处理Pundit 的verify_authorized/verify_policy_scoped用于强制校验每次都调用了 authorize 与 policy_scope但 Active Admin 的授权架构会在内部自己完成这些调用二者冲突。因此如果启用了上述强制校验需要为 Active Admin 的控制器放行class ApplicationController ActionController::Base include Pundit after_action :verify_authorized, except: :index, unless: :active_admin_controller? after_action :verify_policy_scoped, only: :index, unless: :active_admin_controller? def active_admin_controller? is_a?(ActiveAdmin::BaseController) end end批量动作与示例 Policy如果使用批量动作Batch Actions请确保 Policy 中定义了destroy_all?方法。仓库为 Pundit 用户提供了一份可直接使用的模板 Policy比rails g pundit:install生成的默认版更完整其中已包含index?、show?、new?、create?、edit?、update?、destroy?、destroy_all?以及配套的Scope类见 spec/support/templates/policies/application_policy.rb。此外仓库还提供了限制访问 Active Admin 自身页面与评论的示例 Policy 集合可作为编写自己策略的参照见 spec/support/templates/policies/active_admin 目录下的comment_policy.rb与page_policy.rb。小结Active Admin 的授权体系围绕适配器这一抽象展开脉络清晰默认的AuthorizationAdapter全量放行并预留扩展点自定义适配器通过覆写#authorized?实现动作级控制、覆写#scope_collection实现行级数据隔离控制器与视图侧统一使用authorized?/authorize!校验配合on_unauthorized_access处理拒绝与重定向若不想从底层写起直接挂载CanCanAdapter或PunditAdapter即可复用成熟的授权 DSL。无论选择哪种方式动作常量Auth::READ等、页面 subject 约定与命名空间级配置这三个设计点都是理解与扩展该体系的关键。赞分享后端【免费下载链接】activeadminThe administration framework for Ruby on Rails applications.项目地址https://gitcode.com/gh_mirrors/ac/activeadmin点击查看免费下载相关推荐Hubot 适配器Adapter完全指南Shell 与 Campfire 官方适配器实战与自定义适配器开发Hubot 适配器Adapter完全指南Shell 与 Campfire 官方适配器实战与自定义适配器开发 本文是 Hubot 适配器体系的权威实战指南后端交互助手Axios 适配器Adapter完全指南内置适配器选择机制与自定义适配器实战Axios 适配器Adapter完全指南内置适配器选择机制与自定义适配器实战 本文以 axios 仓库中的适配器文档为主体结合源码逐层拆解 axios网络后端前端Spring Authorization Server自定义授权类型实现扩展授权流程的完整指南Spring Authorization Server自定义授权类型实现扩展授权流程的完整指南 Spring Authorization Server 是 S后端认证鉴权身份认证上一篇Go 1.27 encoding/json/v2前瞻go-modern-guidelines带你提前布局下一篇5分钟掌握微信视频号弹幕抓取wxlivespy实战全攻略创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表