能否使用OAuth2 scopes实现模型数据的访问范围控制?
实现方案分步指引
第一步:调整OAuth2 Scope的定义规则
你之前接触的都是纯操作类的scope,针对多企业数据隔离的需求,可以把scope拆成权限维度和租户(企业)维度两类组合使用:
- 操作类权限scope:保持你熟悉的
read、write、post:delete这类格式,控制用户对资源的操作权限 - 租户范围scope:新增
tenant:{企业ID}格式的scope,比如tenant:1001、tenant:1002,代表用户有权限访问对应ID的企业数据
用户登录成功后返回的access token会同时携带这两类scope,比如read write tenant:1001 tenant:1003,就代表用户对ID为1001、1003的企业资源有读写权限。
第二步:OAuth2 认证层逻辑处理
基于Laravel Passport实现的话,直接用自带的scope校验能力即可:
- 先在
AuthServiceProvider的boot方法里注册你定义的操作类scope:
use Laravel\Passport\Passport; Passport::tokensCan([ 'read' => '读取资源权限', 'write' => '写入资源权限', 'post:delete' => '删除帖子权限', // 租户scope为动态生成,不需要提前注册 ]);
- 路由层加中间件校验操作权限,比如读取类路由加
scope:read中间件,写入类路由加scope:write即可。
第三步:Eloquent Global Scope 实现数据自动过滤
这一步可以做到全局自动过滤数据,不需要每个业务查询手动加where条件:
- 先创建全局作用域类
app/Scopes/TenantScope.php:
<?php namespace App\Scopes; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Scope; use Illuminate\Support\Facades\Auth; class TenantScope implements Scope { public function apply(Builder $builder, Model $model) { // 非控制台请求、用户已登录时才加过滤条件 if (!app()->runningInConsole() && Auth::check()) { // 从当前用户的token里提取所有有权限的企业ID $tenantIds = collect(Auth::user()->token()->scopes) ->filter(fn($scope) => str_starts_with($scope, 'tenant:')) ->map(fn($scope) => (int)str_replace('tenant:', '', $scope)) ->toArray(); // 给查询加企业ID过滤条件,默认业务表都有tenant_id字段存储所属企业ID $builder->whereIn($model->getTable() . '.tenant_id', $tenantIds); } } }
- 给所有需要做企业数据隔离的Model注册这个全局作用域,比如帖子模型
app/Models/Post.php:
use App\Scopes\TenantScope; protected static function booted() { static::addGlobalScope(new TenantScope); }
第四步:多企业切换补充处理
如果用户需要指定当前访问的单个企业,可以在请求头里加X-Tenant-ID参数传递要访问的企业ID,在全局作用域里优先提取该ID,校验是否属于用户的租户权限范围后再做过滤,避免单次请求返回多个企业的混合数据。
内容的提问来源于stack exchange,提问作者mowses
相关产品推荐
相关产品推荐

