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

Rails + Grape API权限验证:如何按端点校验Token Scope?

Rails 路由级 Scope 校验方案及 AWS Verified Permissions 集成

一、现成解决方案:Doorkeeper

如果你的 Token 基于 OAuth2 标准,Doorkeeper 这个 gem 原生支持路由级别的 Scope 校验,完全匹配你想要的写法:

配置方式

  1. 在 Gemfile 中添加并安装 Doorkeeper:
gem 'doorkeeper'
  1. 生成配置文件后,直接在路由中指定 required_scopes 参数:
# config/routes.rb
Rails.application.routes.draw do
  get 'endpoint', to: 'api#endpoint', required_scopes: ['custom.scope']
end
  1. Doorkeeper 会自动拦截请求,校验 Token 包含的 Scope 是否覆盖路由要求的范围,不符合时直接返回 403 错误,无需在控制器中编写额外逻辑。

二、自定义实现(无需第三方 gem)

如果不想引入 Doorkeeper,可以通过 Rails 路由自定义选项 + 控制器全局回调实现,完全贴合你的需求:

方案1:路由直接加自定义参数

  1. 在路由中给需要校验的端点添加 required_scopes 选项:
# config/routes.rb
Rails.application.routes.draw do
  get 'user/profile', to: 'users#profile', required_scopes: ['user.read']
  post 'user/update', to: 'users#update', required_scopes: ['user.write']
end
  1. 在 ApplicationController 中添加全局前置回调,统一处理 Scope 校验:
class ApplicationController < ActionController::API
  before_action :validate_required_scopes

  private

  def validate_required_scopes
    # 从当前路由的默认参数中取出要求的 Scope
    required_scopes = request.route&.defaults[:required_scopes]
    return unless required_scopes.present?

    # 替换成你实际获取当前用户 Token Scope 的逻辑
    user_scopes = current_token_scopes

    # 校验用户是否拥有所有要求的 Scope
    unless (required_scopes - user_scopes).empty?
      render json: { error: '权限不足:缺少必要的 Scope' }, status: :forbidden
    end
  end
end

方案2:类似 params 块的 DSL 写法

如果偏好更结构化的 DSL(类似你提到的 claims 块),可以在控制器中自定义 DSL:

  1. 在基控制器中定义 DSL 方法:
class ApiController < ActionController::API
  before_action :validate_required_scopes

  # 自定义 DSL:为指定 action 绑定要求的 Scope
  def self.required_scopes(action_name, scopes)
    @action_scopes ||= {}
    @action_scopes[action_name.to_sym] = scopes
  end

  private

  def validate_required_scopes
    # 根据当前 action 取出对应的 Scope 要求
    required_scopes = self.class.instance_variable_get(:@action_scopes)&.[](action_name.to_sym)
    return unless required_scopes.present?

    user_scopes = current_token_scopes
    unless (required_scopes - user_scopes).empty?
      render json: { error: '权限不足:缺少必要的 Scope' }, status: :forbidden
    end
  end
end
  1. 在具体业务控制器中使用:
class UsersController < ApiController
  required_scopes :profile, ['user.read']
  required_scopes :update, ['user.write']

  def profile
    # 业务逻辑
  end

  def update
    # 业务逻辑
  end
end

三、AWS Verified Permissions 集成方案

后续集成 AWS VP 时,可以沿用类似的路由绑定思路,将权限校验逻辑从业务代码中剥离:

实现步骤

  1. 在路由中添加 AWS VP 所需的资源、动作标识:
# config/routes.rb
Rails.application.routes.draw do
  get 'user/profile', to: 'users#profile', 
      aws_vp_resource: 'api:user:profile', 
      aws_vp_action: 'read'
  
  post 'user/update', to: 'users#update',
      aws_vp_resource: 'api:user:update',
      aws_vp_action: 'write'
end
  1. 在 ApplicationController 中添加前置回调,调用 AWS VP 接口校验权限:
class ApplicationController < ActionController::API
  before_action :validate_aws_vp_permissions

  private

  def validate_aws_vp_permissions
    # 从路由参数中取出 AWS VP 所需的资源和动作
    resource = request.route&.defaults[:aws_vp_resource]
    action = request.route&.defaults[:aws_vp_action]
    return unless resource.present? && action.present?

    # 初始化 AWS Verified Permissions 客户端(替换为你的区域)
    vp_client = Aws::VerifiedPermissions::Client.new(region: 'your-region')

    # 构建授权请求参数(根据你的实体定义调整)
    auth_response = vp_client.is_authorized(
      policy_store_id: 'your-policy-store-id',
      principal: { entity_id: current_user.vp_principal_id, entity_type: 'User' },
      resource: { entity_id: resource, entity_type: 'ApiResource' },
      action: { action_id: action, action_type: 'Action' }
    )

    # 根据 AWS VP 返回的决策处理
    unless auth_response.decision == 'ALLOW'
      render json: { error: '权限拒绝' }, status: :forbidden
    end
  end
end

这样不管是 Scope 校验还是 AWS VP 权限校验,都能统一在全局回调中处理,业务端点无需嵌入校验逻辑,保持代码整洁。

内容的提问来源于stack exchange,提问作者Ryan Pierce Williams

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 07:20:10