权限控制
Univer 提供权限控制能力,用于限制用户对工作簿、工作表、选区的操作。当用户执行无权限动作时,Univer 会中止执行并提示缺失权限。常见场景如:对一片选区设置保护,限制协作者的编辑/查看/复制等能力。
按任务跳转到下方章节。本页集中介绍基础操作、进阶示例、配置与排障;完整 API 定义统一在 Reference 中维护。
核心概念
- 作用范围:权限可以作用在工作簿、工作表、选区三个层级。
- 权限点位:每个功能对应一个权限点位,修改点位即可控制功能是否可用。
- 保护规则:工作表和选区在设置权限点位前,需要先创建保护规则。
本地非持久化权限可直接修改权限点位;持久化接入见 权限服务接入
协同编辑场景下,更新权限点位会同步更新服务端状态:
- false:该权限点至少需要
owner角色 - true:该权限点至少需要
reader角色
基础示例
使用 allowedUsers 示例前,请将 user1、user2 替换为权限服务已返回的真实协作者 ID。
下面按工作簿、工作表、选区三个层级展示最常见的权限设置方式。
工作簿权限代码示例
工作簿层级权限通过 Facade API 直接修改权限点位即可。
以编辑权限为例(其他功能只需替换权限点位,参考权限点位列表 - 工作簿):
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const workbookPermission = fWorkbook.getWorkbookPermission()// 设置工作簿不可编辑await workbookPermission.setPoint(univerAPI.Enum.WorkbookPermissionPoint.Edit, false)要点:
- 工作簿权限点位可以直接设置,不需要创建保护规则。
工作表权限代码示例
工作表和选区相关的权限设置可以通过 Facade API 或命令系统实现。这里以工作表编辑权限举例,其他功能只需替换权限点位,参考权限点位列表 - 工作表。
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const fWorksheet = fWorkbook.getActiveSheet()const worksheetPermission = fWorksheet.getWorksheetPermission()// 创建工作表保护, 允许指定用户编辑const permissionId = await worksheetPermission.protect({ allowedUsers: ['user1', 'user2'], name: 'My Worksheet Protection',})// 如果需要设置权限点位,必须先创建工作表保护await worksheetPermission.setPoint(univerAPI.Enum.WorksheetPermissionPoint.Edit, false)删除工作表权限
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const fWorksheet = fWorkbook.getActiveSheet()const worksheetPermission = fWorksheet.getWorksheetPermission()// 删除工作表保护await worksheetPermission.unprotect()自定义选区权限代码示例
选区的权限同样支持 API 和命令模式。这里以选区编辑权限举例,其他选区功能只需替换权限点位,参考权限点位列表 - 选区。
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const fWorksheet = fWorkbook.getActiveSheet()const fRange = fWorksheet.getRange('A1:B2')const rangePermission = fRange.getRangePermission()// 设置区域保护权限,允许指定用户编辑但不允许他人查看const rule = await rangePermission.protect({ name: 'My protected range', allowViewByOthers: false, allowedUsers: ['user1', 'user2'],})console.log(rule)// 如果需要设置权限点位,必须先创建区域保护。await rule.setPoint(univerAPI.Enum.RangePermissionPoint.Edit, false)await rule.setPoint(univerAPI.Enum.RangePermissionPoint.View, true)删除区域保护权限
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const fWorksheet = fWorkbook.getActiveSheet()const fRange = fWorksheet.getRange('A1:B2')const rangePermission = fRange.getRangePermission()// 删除范围内所有保护规则await rangePermission.unprotect()// 或者删除范围内指定规则const rules = await rangePermission.listRules()if (rules.length > 0) { await rules[0].remove()}常用任务
将工作簿设为只读
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const permission = fWorkbook.getWorkbookPermission()await permission.setMode('viewer')console.log(permission.getSnapshot())管理保护范围
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const fWorksheet = fWorkbook.getActiveSheet()const permission = fWorksheet.getWorksheetPermission()const createdRules = await permission.protectRanges([ { ranges: [fWorksheet.getRange('A1:B2')], options: { name: 'Public area', allowViewByOthers: true }, }, { ranges: [fWorksheet.getRange('C3:D4')], options: { name: 'Private area', allowViewByOthers: false, allowedUsers: ['user1'] }, },])console.log(createdRules)const rules = await permission.listRangeProtectionRules()const firstRule = rules[0]if (firstRule) { await firstRule.updateRanges([fWorksheet.getRange('A1:B3')])}排查单元格权限
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const permission = fWorkbook.getActiveSheet().getWorksheetPermission()console.log(permission.debugCellPermission(0, 0))权限点位列表
不同的功能权限由不同的权限点位控制,修改点位的值即可控制对应功能。具体点位代码请参阅这里。
如果 workbook 的权限控制和 worksheet / range 有交叉,那么必须全部为 true 才能使用。例如一个单元格的编辑权限必须 workbook 和 worksheet 的编辑权限都为 true 才能编辑。
工作簿
| API 枚举 | 对应权限点位类 | 描述 |
|---|---|---|
| univerAPI.Enum.WorkbookPermissionPoint.Edit | WorkbookEditablePermission | 能否编辑 |
| univerAPI.Enum.WorkbookPermissionPoint.View | WorkbookViewPermission | 能否查看 |
| univerAPI.Enum.WorkbookPermissionPoint.Print | WorkbookPrintPermission | 能否打印 |
| univerAPI.Enum.WorkbookPermissionPoint.Export | WorkbookExportPermission | 能否导出 |
| univerAPI.Enum.WorkbookPermissionPoint.Share | WorkbookSharePermission | 能否分享 |
| univerAPI.Enum.WorkbookPermissionPoint.CopyContent | WorkbookCopyPermission | 能否复制 |
| univerAPI.Enum.WorkbookPermissionPoint.DuplicateFile | WorkbookDuplicatePermission | 能否复制文档 |
| univerAPI.Enum.WorkbookPermissionPoint.Comment | WorkbookCommentPermission | 能否评论 |
| univerAPI.Enum.WorkbookPermissionPoint.ManageCollaborator | WorkbookManageCollaboratorPermission | 能否管理协作者 |
| univerAPI.Enum.WorkbookPermissionPoint.CreateSheet | WorkbookCreateSheetPermission | 能否创建工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.DeleteSheet | WorkbookDeleteSheetPermission | 能否删除工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.RenameSheet | WorkbookRenameSheetPermission | 能否重命名工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.MoveSheet | WorkbookMoveSheetPermission | 能否移动工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.HideSheet | WorkbookHideSheetPermission | 能否隐藏工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.CopySheet | WorkbookCopySheetPermission | 能否复制工作表 |
| univerAPI.Enum.WorkbookPermissionPoint.ViewHistory | WorkbookViewHistoryPermission | 能否查看历史记录 |
| univerAPI.Enum.WorkbookPermissionPoint.RecoverHistory | WorkbookRecoverHistoryPermission | 能否恢复历史记录 |
| univerAPI.Enum.WorkbookPermissionPoint.CreateProtection | WorkbookCreateProtectPermission | 能否创建保护 |
| univerAPI.Enum.WorkbookPermissionPoint.InsertRow | WorkbookInsertRowPermission | 能否插入行 |
| univerAPI.Enum.WorkbookPermissionPoint.InsertColumn | WorkbookInsertColumnPermission | 能否插入列 |
| univerAPI.Enum.WorkbookPermissionPoint.DeleteRow | WorkbookDeleteRowPermission | 能否删除行 |
| univerAPI.Enum.WorkbookPermissionPoint.DeleteColumn | WorkbookDeleteColumnPermission | 能否删除列 |
工作表
| API 枚举 | 对应权限点位类 | 描述 |
|---|---|---|
| univerAPI.Enum.WorksheetPermissionPoint.Edit | WorksheetEditPermission | 能否编辑 |
| univerAPI.Enum.WorksheetPermissionPoint.View | WorksheetViewPermission | 能否查看 |
| univerAPI.Enum.WorksheetPermissionPoint.Copy | WorksheetCopyPermission | 能否复制 |
| univerAPI.Enum.WorksheetPermissionPoint.SetCellValue | WorksheetSetCellValuePermission | 能否编辑单元格值 |
| univerAPI.Enum.WorksheetPermissionPoint.SetCellStyle | WorksheetSetCellStylePermission | 能否编辑单元格样式 |
| univerAPI.Enum.WorksheetPermissionPoint.SetRowStyle | WorksheetSetRowStylePermission | 能否设置行样式 |
| univerAPI.Enum.WorksheetPermissionPoint.SetColumnStyle | WorksheetSetColumnStylePermission | 能否设置列样式 |
| univerAPI.Enum.WorksheetPermissionPoint.InsertRow | WorksheetInsertRowPermission | 能否插入行 |
| univerAPI.Enum.WorksheetPermissionPoint.InsertColumn | WorksheetInsertColumnPermission | 能否插入列 |
| univerAPI.Enum.WorksheetPermissionPoint.DeleteRow | WorksheetDeleteRowPermission | 能否删除行 |
| univerAPI.Enum.WorksheetPermissionPoint.DeleteColumn | WorksheetDeleteColumnPermission | 能否删除列 |
| univerAPI.Enum.WorksheetPermissionPoint.Sort | WorksheetSortPermission | 能否排序 |
| univerAPI.Enum.WorksheetPermissionPoint.Filter | WorksheetFilterPermission | 能否筛选 |
| univerAPI.Enum.WorksheetPermissionPoint.PivotTable | WorksheetPivotTablePermission | 能否使用透视表 |
| univerAPI.Enum.WorksheetPermissionPoint.InsertHyperlink | WorksheetInsertHyperlinkPermission | 能否使用超链接 |
| univerAPI.Enum.WorksheetPermissionPoint.ManageCollaborator | WorksheetManageCollaboratorPermission | 能否管理协作者 |
| univerAPI.Enum.WorksheetPermissionPoint.DeleteProtection | WorksheetDeleteProtectionPermission | 能否删除保护 |
| univerAPI.Enum.WorksheetPermissionPoint.EditExtraObject | WorksheetEditExtraObjectPermission | 能否编辑额外对象 |
| univerAPI.Enum.WorksheetPermissionPoint.SelectProtectedCells | WorksheetSelectProtectedCellsPermission | 能否选择受保护单元格 |
| univerAPI.Enum.WorksheetPermissionPoint.SelectUnProtectedCells | WorksheetSelectUnProtectedCellsPermission | 能否选择未受保护单元格 |
区域保护
| API 枚举 | 对应权限点位类 | 描述 |
|---|---|---|
| univerAPI.Enum.RangePermissionPoint.Edit | RangeProtectionPermissionEditPoint | 能否编辑保护区域 |
| univerAPI.Enum.RangePermissionPoint.View | RangeProtectionPermissionViewPoint | 能否查看保护区域的内容 |
| univerAPI.Enum.RangePermissionPoint.Delete | RangeProtectionPermissionDeleteProtectionPoint | 能否删除保护区域 |
| univerAPI.Enum.RangePermissionPoint.ManageCollaborator | RangeProtectionPermissionManageCollaPoint | 能否管理保护区域的协作者 |
权限界面配置
权限保护范围阴影策略
如果需要区分当前用户可编辑和不可编辑的保护区域,使用 non-editable:可编辑的保护区域不显示阴影,编辑权限被禁用的保护区域保留阴影;不可查看的保护范围仍保留遮罩。
此设置控制范围保护和工作表保护的显示,不会创建保护规则或修改编辑、查看权限,也不会给未受保护的区域增加阴影。请先按上文配置保护规则和当前用户的权限。
| 策略 | 显示效果 |
|---|---|
true / 'always' | 显示所有保护区域的阴影(默认)。 |
'non-editable' | 隐藏可编辑保护区域的阴影,保留不可编辑区域的阴影。 |
'non-viewable' | 仅显示不可查看保护区域的阴影。 |
false / 'none' | 隐藏全部保护阴影,权限检查仍然生效。 |
权限阴影示意
切换策略,比较同一组区域对当前用户的显示效果。
未保护
无阴影
已保护 · 可编辑
无阴影
已保护 · 只读
显示阴影
已保护 · 不可查看
显示阴影
此示意只改变阴影,不改变权限。即使选择 none,不可查看的内容也不会显示。
初始化时设置:
univer.registerPlugin(UniverSheetsUIPlugin, { protectedRangeShadow: 'non-editable',})createUniver({ presets: [ UniverSheetsCorePreset({ sheets: { protectedRangeShadow: 'non-editable', }, }), ],})也可以通过 API 动态修改:
下方 Facade API 会更新当前 Univer 实例中已有工作簿的显示策略。插件模式下需先导入 @univerjs/sheets-ui/facade;Sheets Core 预设已包含该导入。
// 设置只显示不可编辑范围的阴影univerAPI.setProtectedRangeShadowStrategy('non-editable')// 获取当前的权限保护范围阴影显示策略console.log(univerAPI.getProtectedRangeShadowStrategy())// 监听权限保护范围阴影显示策略的变化const subscription = univerAPI.getProtectedRangeShadowStrategy$().subscribe((strategy) => { console.log('Global strategy changed to:', strategy) // 更新 UI 或执行其他操作})// 稍后,取消订阅以进行清理subscription.unsubscribe()自定义用户组件
Univer 内置的自定义用户组件所适配的场景比较有限,如果需要更复杂的自定义组件,可以通过以下方式进行自定义。
可在《自定义组件》查看如何自定义组件。
univer.registerPlugin(UniverSheetsUIPlugin, { protectedRangeUserSelector: { /** * custom component, should implement the `IPermissionDetailUserPartProps` interface. */ component: CustomPermissionDetailUserPart, /** * The framework of the component. Must be passed correctly. */ framework: 'react', },})const { univer } = createUniver({ presets: [ UniverSheetsCorePreset({ sheets: { protectedRangeUserSelector: { /** * custom component, should implement the `IPermissionDetailUserPartProps` interface. */ component: CustomPermissionDetailUserPart, /** * The framework of the component. Must be passed correctly. */ framework: 'react', }, }, }), ],})在用户选择器的变更回调中调用 setSelectUserList()。下面的最小 React 示例清空已选用户:useDependency 在组件内调用,服务状态只在点击后更新。完整选择器请按 IPermissionDetailUserPartProps 实现。
import type { IPermissionDetailUserPartProps } from '@univerjs/sheets-ui'import type { FC } from 'react'import { SheetPermissionUserManagerService } from '@univerjs/sheets-ui'import { useDependency } from '@univerjs/ui'export const CustomPermissionDetailUserPart: FC<IPermissionDetailUserPartProps> = () => { const service = useDependency(SheetPermissionUserManagerService) const clearSelection = () => service.setSelectUserList([]) return <button type="button" onClick={clearSelection}>清空已选用户</button>}import type { IPermissionDetailUserPartProps } from '@univerjs/preset-sheets-core'import type { FC } from 'react'import { SheetPermissionUserManagerService, useDependency } from '@univerjs/preset-sheets-core'export const CustomPermissionDetailUserPart: FC<IPermissionDetailUserPartProps> = () => { const service = useDependency(SheetPermissionUserManagerService) const clearSelection = () => service.setSelectUserList([]) return <button type="button" onClick={clearSelection}>清空已选用户</button>}去除权限弹窗
univerAPI.setPermissionDialogVisible(false)权限服务接入
拓展使用
当你需要在自己的插件中增加权限校验时,可以直接操作权限点位。
下面以 WorkbookEditablePermission 为例,其他点位类似:
import { IPermissionService } from '@univerjs/core'import { WorkbookEditablePermission } from '@univerjs/sheets'class YourService { constructor(@IPermissionService private _permissionService: IPermissionService) { } setWorkbookNotEditable() { this._permissionService.updatePermissionPoint(new WorkbookEditablePermission('unitId').id, false) } setWorkbookEditable() { this._permissionService.updatePermissionPoint(new WorkbookEditablePermission('unitId').id, true) }}第三方插件如何扩展权限点位
如果你需要自定义权限点位,可以实现 IPermissionPoint 并注册到权限服务中:
import type { IPermissionPoint } from '@univerjs/core'import { IPermissionService, PermissionStatus } from '@univerjs/core'import { UnitAction, UnitObject } from '@univerjs/protocol'export class CustomPermissionPoint implements IPermissionPoint { type = UnitObject.Unkonwn // your type subType = UnitAction.View // your subType status = PermissionStatus.INIT value = true // 权限的初始值 id: string constructor(unitId: string, subUnitId: string, customId: string) { // 这里自行拼凑一个 id,id 属性需要保证在整个 IPermissionService 中是唯一的凭证。 this.id = `${unitId}.${subUnitId}.${customId}` }}class YourService { constructor(@IPermissionService private _permissionService: IPermissionService) { this._init() } _init() { this._permissionService.addPermissionPoint(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id')) }}// 在其他地方如何使用class ConsumeService { constructor(@IPermissionService private _permissionService: IPermissionService) { } doSomething() { const point = this._permissionService.getPermissionPoint(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id').id) console.log(point?.value) } bindEvent() { // 这将获得一个 Rx 对象,使得你能够监听当前权限的变化作出一些改变 const point$ = this._permissionService.getPermissionPoint$(new CustomPermissionPoint('unitId', 'subUnitId', 'my-id').id) console.log(point$) }}第三方权限服务接入(进阶用法)
在业务应用中实现 IAuthzIoService,将权限、角色和协作者方法接入后端。以下示例假定 ./authorization-service 已将该实现导出为 YourAuthzService。在创建工作簿前注册它,并保留其他初始化选项和插件注册。
import { IAuthzIoService, Univer } from '@univerjs/core'import { YourAuthzService } from './authorization-service'const univer = new Univer({ override: [[IAuthzIoService, { useClass: YourAuthzService }]],})服务需要持久化保护规则和协作者变更,返回当前用户的权限,并传递请求失败。返回空列表的 mock 不具备持久化能力。协同场景下,后端还须分别校验读取、JOIN 和修改权限,见身份与权限。前端保护和隐藏阴影不能代替服务端鉴权。
协作者管理
协作者管理由业务应用和权限服务负责。请接入自己的用户与 ACL 模型,并在服务端分别校验读取、JOIN 和修改权限,见身份与权限。下方前端 API 需要匹配的权限服务实现,不会自动配置服务端权限。
- 获取协作者列表
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const workbookPermission = fWorkbook.getWorkbookPermission()const collaborators = await workbookPermission.listCollaborators()console.log(collaborators)- 添加协作者
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const workbookPermission = fWorkbook.getWorkbookPermission()// 一次添加多个协作者await workbookPermission.setCollaborators([ { user: { userID: 'user1', name: 'John Doe', avatar: 'https://...' }, role: univerAPI.Enum.UnitRole.Editor, }, { user: { userID: 'user2', name: 'Jane Smith', avatar: '' }, role: univerAPI.Enum.UnitRole.Reader, },])// 添加单个协作者await workbookPermission.addCollaborator( { userID: 'user1', name: 'John Doe', avatar: 'https://...' }, univerAPI.Enum.UnitRole.Editor,)- 更新协作者角色
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const workbookPermission = fWorkbook.getWorkbookPermission()await workbookPermission.updateCollaborator( { userID: 'user1', name: 'John Doe Updated', avatar: 'https://...' }, univerAPI.Enum.UnitRole.Reader,)- 删除协作者
const fWorkbook = univerAPI.getActiveWorkbook()if (!fWorkbook) throw new Error('No active workbook')const workbookPermission = fWorkbook.getWorkbookPermission()// 删除多个协作者await workbookPermission.removeCollaborators(['user1', 'user2'])// 删除单个协作者await workbookPermission.removeCollaborator('user1')Reference
你觉得这篇文档如何?