能否针对特定用户角色生成对应权限的OpenAPI接口文档?
API Platform基于角色生成对应权限的OpenAPI文档方案
结论
这个需求完全可以实现,API Platform预留了OpenAPI文档的自定义扩展能力,你可以通过重写文档生成逻辑,结合当前登录用户的角色做过滤即可。
核心实现思路
通过自定义OpenApiFactory装饰器,在原有自动生成的OpenAPI文档基础上,做两层过滤:
- 第一层:过滤掉当前用户角色无权限访问的接口路径
- 第二层:过滤掉接口返回Schema中当前用户角色无权限查看的属性
具体操作步骤
- 第一步:创建自定义OpenAPI工厂类
在项目的src/OpenApi/目录下新建RoleBasedOpenApiFactory.php文件,代码示例如下:
<?php namespace App\OpenApi; use ApiPlatform\OpenApi\Factory\OpenApiFactoryInterface; use ApiPlatform\OpenApi\OpenApi; use ApiPlatform\OpenApi\Model\PathItem; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface; use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface; class RoleBasedOpenApiFactory implements OpenApiFactoryInterface { public function __construct( private OpenApiFactoryInterface $decorated, private TokenStorageInterface $tokenStorage, private AuthorizationCheckerInterface $authorizationChecker ) {} public function __invoke(array $context = []): OpenApi { $openApi = ($this->decorated)($context); $token = $this->tokenStorage->getToken(); if (!$token || !$token->getUser()) { // 匿名用户逻辑,只返回公开接口 return $this->filterPublicPaths($openApi); } // 过滤无权限的接口路径 $paths = $openApi->getPaths()->getPaths(); $filteredPaths = new \ArrayObject(); foreach ($paths as $path => $pathItem) { if ($this->isPathAllowed($pathItem, $token)) { $filteredPaths[$path] = $pathItem; } } $openApi = $openApi->withPaths(new \ApiPlatform\OpenApi\Model\Paths($filteredPaths)); // 过滤Schema里无权限的属性 $components = $openApi->getComponents(); $schemas = $components->getSchemas(); $filteredSchemas = new \ArrayObject(); foreach ($schemas as $schemaName => $schema) { $filteredSchema = $this->filterSchemaProperties($schema, $token); $filteredSchemas[$schemaName] = $filteredSchema; } $openApi = $openApi->withComponents($components->withSchemas($filteredSchemas)); return $openApi; } private function isPathAllowed(PathItem $pathItem, $token): bool { // 替换为你自己的接口权限校验逻辑,比如读取该路径ApiResource的security配置校验 // 示例:如果路径需要ROLE_ADMIN,当前用户没有就返回false return true; } private function filterSchemaProperties($schema, $token): object { // 替换为你自己的属性权限校验逻辑,过滤掉当前用户无权限查看的属性 return $schema; } private function filterPublicPaths(OpenApi $openApi): OpenApi { // 替换为你自己的公开接口过滤逻辑 return $openApi; } }
- 第二步:注册装饰器服务
在config/services.yaml中添加服务配置,将自定义类注册为原生OpenAPI工厂的装饰器:
# config/services.yaml services: App\OpenApi\RoleBasedOpenApiFactory: decorates: 'api_platform.openapi.factory' arguments: ['@.inner'] # 优先级可按需调整,数值越高越先执行 priority: -10
- 第三步:实现你的权限校验逻辑
上面代码中的isPathAllowed、filterSchemaProperties方法需要你根据自己项目的权限规则实现:- 如果你是通过
ApiResource注解的security属性配置的接口权限,可以直接读取该路径对应资源的权限表达式,通过AuthorizationCheckerInterface校验当前用户是否有权限 - 如果你是通过
ApiProperty注解的security属性配置的属性权限,读取对应属性的权限表达式校验后,从Schema的属性列表中移除无权限的属性,同时同步更新Schema的required字段
- 如果你是通过
- 第四步:适配匿名用户场景
如果你的接口支持匿名访问,在filterPublicPaths方法中实现公开接口的过滤逻辑即可。
注意事项
要额外注意OpenAPI文档的缓存问题:默认API Platform会缓存生成的OpenAPI内容,如果你需要不同角色看到不同文档,需要修改缓存配置,将用户角色加入缓存键,或者在开发环境关闭缓存,生产环境按角色做缓存分区,避免不同权限的用户拿到同一份缓存文档。
内容的提问来源于stack exchange,提问作者RedBeard
相关产品推荐
相关产品推荐

