Rails + Grape API权限验证:如何按端点校验Token Scope?
Rails 路由级 Scope 校验方案及 AWS Verified Permissions 集成
一、现成解决方案:Doorkeeper
如果你的 Token 基于 OAuth2 标准,Doorkeeper 这个 gem 原生支持路由级别的 Scope 校验,完全匹配你想要的写法:
配置方式
- 在
Gemfile中添加并安装 Doorkeeper:
gem 'doorkeeper'
- 生成配置文件后,直接在路由中指定
required_scopes参数:
# config/routes.rb Rails.application.routes.draw do get 'endpoint', to: 'api#endpoint', required_scopes: ['custom.scope'] end
- Doorkeeper 会自动拦截请求,校验 Token 包含的 Scope 是否覆盖路由要求的范围,不符合时直接返回 403 错误,无需在控制器中编写额外逻辑。
二、自定义实现(无需第三方 gem)
如果不想引入 Doorkeeper,可以通过 Rails 路由自定义选项 + 控制器全局回调实现,完全贴合你的需求:
方案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
- 在
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:
- 在基控制器中定义 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
- 在具体业务控制器中使用:
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 时,可以沿用类似的路由绑定思路,将权限校验逻辑从业务代码中剥离:
实现步骤
- 在路由中添加 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
- 在
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
相关产品推荐
相关产品推荐

