news 2026/9/10 15:26:16

Backstage Permissions Registry 核心服务详解:从 `createPermissionIntegrationRouter` 迁移到新式插件注册

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Backstage Permissions Registry 核心服务详解:从 `createPermissionIntegrationRouter` 迁移到新式插件注册

Backstage Permissions Registry 核心服务详解:从createPermissionIntegrationRouter迁移到新式插件注册

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

本篇文章深入解析 Backstage 后端系统(New Backend System)中的permissionsRegistry核心服务,它是插件向权限框架注册权限(permissions)、规则(rules)与资源类型(resource types)的统一入口。你会掌握该服务的全部接口方法、完整的迁移路径(从旧的createPermissionIntegrationRouter迁移过来)、底层实现原理,以及软件目录插件(catalog)如何实际使用它,为你的插件接入细粒度授权能力提供可直接落地的方案。

Permissions Registry 服务在权限体系中的定位

Backstage 的权限框架允许你对插件功能进行细粒度的访问控制。要让一个插件的能力参与授权决策,插件必须先把三类元数据告知权限系统:权限(Permission,如 "查看实体")、规则(Permission Rule,如 "实体所有者是 team-a")以及资源类型(Resource Type,如catalog-entity)。

permissionsRegistry正是承载这一注册职责的核心服务。根据官方描述,它"允许你的插件注册新的权限、规则和资源类型,并与权限框架集成",是 权限框架总览 与后端插件之间的桥梁。

在源码层面,该服务的依赖定义位于 coreServices.ts,服务引用标识为core.permissionsRegistry

export const permissionsRegistry = createServiceRef< import('./PermissionsRegistryService').PermissionsRegistryService >({ id: 'core.permissionsRegistry' });

也就是说,任何基于新后端系统的插件,都可以在registerInitdeps中直接声明coreServices.permissionsRegistry并注入使用,无需额外的配置或初始化步骤。

服务接口:四个核心方法

服务接口定义在 PermissionsRegistryService.ts,共包含四个方法:

方法作用适用场景
addPermissions(permissions: Permission[])向权限系统注册一批权限只注册权限、不涉及条件规则时
addPermissionRules(rules)为某个资源类型注册条件过滤规则补充自定义规则(可由插件自身或插件模块注册)
addResourceType(options)注册插件拥有的资源类型,并绑定权限与规则需要支持条件授权(conditional decisions)的插件
getPermissionRuleset(resourceRef)取回该资源已注册的规则集配合createConditionAuthorizer/createConditionTransformer使用

addResourceType的完整选项

addResourceType是核心方法,其选项类型PermissionsRegistryServiceAddResourceTypeOptions包含以下字段:

  • resourceRef(必填):标识资源类型的PermissionResourceRef,内部携带pluginIdresourceType两个关键信息。
  • permissions(可选):该资源类型下可用的权限列表。
  • rules(必填):该资源类型的条件过滤规则数组,类型为PermissionRule<TResource, TQuery, TResourceType>[]。例如软件目录中的isEntityOwnerhasAnnotation,规则描述如何过滤一组资源,并允许以具体参数(如group:default/team-abackstage.io/edit-url)实例化条件。
  • getResources(可选):根据资源引用标识批量加载资源对象的函数。注释明确说明:如果不提供此函数,权限系统将无法解析条件决策,除非直接从插件请求资源。它是permission-backend在评估与当前插件相关的授权条件时回调的入口。

以软件目录为例(见 PermissionsRegistryService.ts 中的说明):catalog 围绕具体的实体(entities)做条件访问控制,其资源类型标识符为catalog-entity;这个标识符只用于校验授权策略中的条件构造是否正确,而非指向某个具体资源的引用。

在插件中注册权限与资源类型

要使用该服务,只需在插件初始化时声明依赖并调用相应方法。例如一个拥有自定义资源类型的插件可以这样写:

import { coreServices, createBackendPlugin } from '@backstage/backend-plugin-api'; export const examplePlugin = createBackendPlugin({ pluginId: 'example', register(env) { env.registerInit({ deps: { logger: coreServices.logger, permissionsRegistry: coreServices.permissionsRegistry, }, async init({ logger, permissionsRegistry }) { logger.log('This is a silly example plugin with no functionality'); permissionsRegistry.addResourceType({ resourceRef: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], getResources: async resourceRefs => { // 根据引用标识加载资源,供权限后端评估条件使用 return resourceRefs.map(ref => loadResource(ref)); }, }); }, }); }, });

关于更完整的插件作者权限指南,可参考 权限指南(插件作者篇);如果只想为已有插件添加自定义权限规则,可直接查阅 自定义权限规则指南。

createPermissionIntegrationRouter迁移

在新后端系统引入本服务之前,插件通过createPermissionIntegrationRouter实现同样的注册功能,并把返回的 router 挂载到自己的 HTTP 路由上。迁移的核心思路是:删除对createPermissionIntegrationRouter的调用,但把它收到的所有选项原样转交给permissionsRegistry服务

第一步:移除旧路由注册

export async function createRouter() { const router = Router(); // 删除以下代码块 const permissionIntegrationRouter = createPermissionIntegrationRouter({ resourceType: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], }); router.use(permissionIntegrationRouter); // ... }

第二步:注入服务并注册相同选项

export const examplePlugin = createBackendPlugin({ pluginId: 'example', register(env) { env.registerInit({ deps: { logger: coreServices.logger, permissionsRegistry: coreServices.permissionsRegistry, // 新增依赖 }, async init({ logger, permissionsRegistry }) { logger.log('This is a silly example plugin with no functionality'); // 与旧的 createPermissionIntegrationRouter 选项一一对应 permissionsRegistry.addResourceType({ resourceType: RESOURCE_TYPE_MY_RESOURCE, permissions: [myResourcePermissions], rules: [myResourceRule], }); }, }); }, });

第三步:按选项形态选择迁移方式

官方文档针对不同选项形态给出了三种迁移映射:

  • 如果旧代码只传了permissions选项(不涉及资源类型与规则),应改用permissionsRegistry.addPermissions而非addResourceType
  • 如果旧代码通过resources选项传入了多个资源类型,则应对每个资源类型分别调用一次permissionsRegistry.addResourceType
  • 如果旧代码通过rules注册了规则,对应使用addResourceType中的rules字段(或独立调用addPermissionRules)。

这种"选项平移到服务方法"的设计,使得迁移过程几乎是机械式的替换,插件无需重写任何权限逻辑。

底层实现:服务工厂如何工作

默认的服务工厂实现位于 permissionsRegistryServiceFactory.ts,它揭示了几个值得注意的实现细节:

1. 内部仍然封装createPermissionIntegrationRouter

工厂内部直接创建了一个createPermissionIntegrationRouter(),所有注册方法(addResourceTypeaddPermissionsaddPermissionRules)最终都转发给这个 router 实例。这保证了迁移前后行为完全一致——旧 API 的语义被完整保留,只是注册入口从"手动挂载路由"变成了"核心服务"

2. 资源归属校验(pluginId 强绑定)

工厂中的assertRefPluginId函数会校验传入的PermissionResourceRef.pluginId是否与当前插件的pluginId一致:

function assertRefPluginId(ref: PermissionResourceRef, pluginId: string) { if (ref.pluginId !== pluginId) { throw new Error( `Resource type '${ref.resourceType}' belongs to plugin '${ref.pluginId}', but was used with plugin '${pluginId}'`, ); } }

也就是说,插件不能注册属于其他插件的资源类型,这一约束在addResourceTypegetPermissionRuleset两个入口都会强制执行,从源头避免了插件之间资源声明的互相污染。

3. 注册锁定:启动后禁止再注册

工厂通过lifecycle.addStartupHook记录启动状态:

let started = false; lifecycle.addStartupHook(() => { started = true; });

一旦插件启动完成,任何addResourceType/addPermissions/addPermissionRules调用都会抛出Cannot add permission resource types after the plugin has started之类的错误。注册必须在插件初始化阶段完成,这也意味着插件模块(modules)同样可以在启动前通过该服务补充规则。

4. 自动挂载条件应用端点

工厂将内部 router 挂载到coreServices.httpRouter,并额外在/.well-known/backstage/permissions/apply-conditions路径挂载了鉴权中间件:该端点只允许userservice主体访问,且user 主体必须带有真实身份(actor),否则抛出NotAllowedError。这是permission-backend在评估与插件相关的授权条件时调用的服务端 API,也是getResources回调被触发的通道。

仓库中的真实用例:软件目录插件

最典型的落地案例是软件目录插件。在 CatalogBuilder.ts 中,catalog 构建其权限注册时:

const getResources = async (resourceRefs: string[]) => { const { items } = await unauthorizedEntitiesCatalog.entitiesBatch({ credentials: await auth.getOwnServiceCredentials(), entityRefs: resourceRefs, }); return entitiesResponseToObjects(items).map(e => e || undefined); }; permissionsRegistry.addResourceType({ resourceRef: catalogEntityPermissionResourceRef, getResources, permissions: [...catalogPermissions], rules: Object.values(catalogPermissionRules), });

从中可以看到几个实践要点:

  • getResources使用服务自身的凭据(auth.getOwnServiceCredentials())通过entitiesBatch批量加载实体,避免绕过授权校验;
  • 同一处还通过permissionsRegistry.getPermissionRuleset(...)取得规则集,供条件授权器(createConditionAuthorizer)与条件转换器(createConditionTransformer)使用(见 CatalogBuilder.ts)。

测试验证:跨插件注册被拒绝

服务工厂的测试用例 permissionsRegistryServiceFactory.test.ts 使用startTestBackend验证了资源归属约束:当插件test试图注册pluginId: 'other'的资源类型(或读取其规则集)时,后端启动会直接失败并抛出:

Plugin 'test' startup failed; caused by Error: Resource type 'some-resource' belongs to plugin 'other', but was used with plugin 'test'

这一测试从行为层面确认了上述"pluginId 强绑定"约束的真实性与重要性,是你在自研插件中编写类似注册逻辑时值得参考的边界条件。

小结与推荐路径

permissionsRegistry是新后端系统下插件接入权限框架的标准姿势。你可以据此规划自己的接入路线:

  1. 只注册权限:使用addPermissions
  2. 为已有资源类型补充规则:使用addPermissionRules(插件模块同样适用);
  3. 拥有自己的资源类型并支持条件授权:使用addResourceType,务必提供getResources以便permission-backend解析条件决策;
  4. 从旧系统迁移:将createPermissionIntegrationRouter的选项逐一平移到上述服务方法,删除手动router.use

如需更体系化的学习,可继续阅读 权限框架总览、插件作者权限指南 与 自定义权限规则指南。

【免费下载链接】backstageBackstage is an open framework for building developer portals项目地址: https://gitcode.com/GitHub_Trending/ba/backstage

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/10 15:20:04

TVBoxOSC 语音控制 5 分钟上手指南:3 步开口即操作

TVBoxOSC 语音控制 5 分钟上手指南&#xff1a;3 步开口即操作 【免费下载链接】TVBoxOSC TVBoxOSC - 一个基于第三方项目的代码库&#xff0c;用于电视盒子的控制和管理。 项目地址: https://gitcode.com/GitHub_Trending/tv/TVBoxOSC 坐在沙发上不用碰遥控器&#xff…

作者头像 李华
网站建设 2026/9/10 15:18:54

电磁仿真软件选型与应用全解析

1. 电磁仿真软件行业现状与核心需求电磁场仿真技术作为现代电子工程设计的基石&#xff0c;已经渗透到通信设备、汽车电子、航空航天等各个领域。根据2023年EDA行业报告显示&#xff0c;全球电磁仿真软件市场规模已突破50亿美元&#xff0c;年复合增长率保持在12%以上。这种快速…

作者头像 李华
网站建设 2026/9/10 15:18:31

微服务架构疫苗预约系统实战:从Spring Boot到云部署全解析

先交代一下这个项目的来源。上半年我帮一个社区接种点做信息化改造&#xff0c;他们当时的预约方式是微信群接龙加现场排队&#xff0c;每天早上八点半放号&#xff0c;手机一响所有人同时点&#xff0c;页面直接卡死。后来我以这个真实场景为蓝本&#xff0c;用 Spring Boot 做…

作者头像 李华
网站建设 2026/9/10 15:17:38

基于Python的图像信息隐藏与LSB隐写算法毕业设计解析

简介&#xff1a;这份毕业设计项目资料面向计算机相关专业学生&#xff0c;围绕Python图像信息隐藏技术&#xff0c;提供可运行源码、MySQL数据库及说明文档&#xff0c;适合毕业设计选题、课程设计参考以及图像隐写算法入门实践。系统采用Python和MySQL开发&#xff0c;内容覆…

作者头像 李华