Rails 6下单一路由结合CanCanCan实现个人账户访问授权问题
解决方案
你遇到的核心问题是单资源路由resource :account不会生成:id路由参数,CanCanCan默认的资源加载逻辑找不到对应加载对象,以下两个方案都可以在不手动调用Account.find、完全复用现有授权规则的前提下解决问题:
方案1:补全路由参数(最简洁,兼容性最好)
只需要给控制器加一个前置钩子,在CanCanCan加载资源前给单资源场景补全params[:id]即可,原有load_and_authorize_resource配置完全不用修改:
# app/controllers/accounts_controller.rb class AccountsController < ApplicationController # 保留原有配置不变 load_and_authorize_resource # 仅针对单资源路由对应的动作生效,如有update等其他动作可自行扩展only范围 before_action :inject_account_id_for_singleton_route, only: :show # 原有业务逻辑完全不变 private def inject_account_id_for_singleton_route # 没有传入id参数时(即单资源/account访问场景),自动赋值为当前用户的账户ID params[:id] ||= current_user.account_id end end
优势:无侵入性,同时兼容原有/accounts/:id和新的/account两条路由,授权逻辑完全走你已经定义的权限规则,不会出现越权风险。
方案2:自定义CanCanCan加载规则
如果你不想加额外的前置钩子,可以直接修改load_and_authorize_resource的配置,自定义资源查找逻辑:
# app/controllers/accounts_controller.rb class AccountsController < ApplicationController load_and_authorize_resource find_by: ->(controller) { # 有id参数时用id查找,无id参数时用当前用户的账户ID查找 controller.params[:id].present? ? { id: controller.params[:id] } : { id: controller.current_user.account_id } } # 原有业务逻辑完全不变 end
注意事项:需要确保这部分逻辑执行前current_user已经完成初始化,务必把用户鉴权前置钩子(比如devise的authenticate_user!)放在load_and_authorize_resource之前执行。
内容的提问来源于stack exchange,提问作者almo
相关产品推荐
相关产品推荐

