Rails使用CanCan配置权限后RSpec越权创建AcademicHistory测试失败
越权创建问题的根因定位
其他账号可无权限创建AcademicHistory,是两处配置与CanCanCan默认逻辑不匹配,导致权限校验链路完全放行:
- 父资源权限校验错位
load_and_authorize_resource :account处理嵌套子资源的create动作时,默认对父资源Account执行:read权限校验。但Ability中配置了无限制的can :read, Account规则,意味着任何账号都可读取任意Account,即使路由传入其他用户的Account ID,这步检查也会直接放行,不会触发403拦截。 - 子资源权限校验被shallow模式绕过
给AcademicHistory配置的shallow: true选项,会在嵌套路由场景下跳过父资源关联校验:- 新建的AcademicHistory对象不会自动绑定路由传入的Account ID
- 权限检查时,CanCan无法匹配
can :manage, AcademicHistory, account_id: account.id的条件规则,最终因无法识别越权行为直接放行
修复方案
- 调整控制器资源加载配置
移除shallow模式的绕过逻辑,强制走完整的关联加载和权限校验流程:# frozen_string_literal: true module V1 # AcademicHistoriesController class AcademicHistoriesController < ApplicationController load_and_authorize_resource :account # 禁用shallow模式,强制通过Account关联加载AcademicHistory资源 load_and_authorize_resource through: :account, shallow: false def create @academic_history.save! render json: @academic_history, status: :created end private def resource_params params.require(:academic_history).permit( :name, :faculty, :since_date, :until_date, :type ) end end end - 强化Ability的权限匹配规则
补充关联级的权限校验逻辑,适配嵌套路由下的新建资源校验场景,避免仅靠外键属性匹配因资源未绑定而失效:# frozen_string_literal: true # Ability class Ability include CanCan::Ability def initialize(account) account ||= Account.new can :read, Account can :manage, Account, id: account.id can :manage, Profile, account_id: account.id can :read, AcademicHistory # 补充关联级校验,确保仅所属账号可管理自身的AcademicHistory can :manage, AcademicHistory, account: { id: account.id } end end
修复后校验逻辑
- 当用户尝试访问其他账号的创建接口时:先加载目标Account资源,因当前用户仅能管理自己的Account,权限检查失败,直接返回403,不会执行资源保存
- 当用户访问自身账号的创建接口时:资源自动绑定当前登录账号ID,权限检查通过,正常完成创建流程
- 其他用户读取AcademicHistory的规则不受影响,保持原有开放读取的逻辑
内容的提问来源于stack exchange,提问作者Nozomi Iida
相关产品推荐
相关产品推荐

