定义数据权限

Fabric应用使用 @role 修饰器将授权规则直接附加到数据模型。 权限具有类型安全性,便于重构,并会自动编译到底层数据访问配置中。

在您开始之前

  • 了解身份验证(你是谁)和授权之间的差异(可以执行的操作)
  • 参阅 配置身份验证 以设置身份验证机制
  • 通过数据模型概述了解实体基础知识

内置角色

Fabric 应用识别内置的 authenticated 角色。 还可以根据需要在策略中定义自定义角色。

角色 说明 用例
authenticated 需要具有Fabric身份验证的有效用户会话 用户特定的数据,受保护的资源

@role修饰器

在类级别应用 @role 以控制哪些角色可以对实体执行哪些操作:

@role(roleName, actions, options?)

Parameters

参数 类型 说明
roleName string 角色名称,例如 'authenticated' 或自定义应用程序角色
actions string \| string[] 单个操作或操作数组:'create''read''update''delete',或全部使用 '*'
options object 具有 checkincludeexclude 属性的可选对象

基本示例

将已通过身份验证的用户的访问权限限制为仅访问其自己的数据:

import { entity, role, uuid, text } from '@microsoft/rayfin-core';

@entity()
@role('authenticated', ['create', 'read', 'update', 'delete'], {
  policy: (claims, item) => claims.sub.eq(item.userId),
})
export class Todo {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) description?: string;
  @text() userId!: string;
}

在本示例中:

  • 经过身份验证的用户只能访问与 JWT userId 声明匹配的 sub Todo 项。

类型安全的策略表达式

policy 回调提供对声明和实体字段的类型化访问。 TypeScript 从修饰类推断实体类型,从而提供自动完成和重构安全性:

policy: (claims, item) => claims.sub.eq(item.userId)

支持的声明

索赔 说明 示例值
claims.sub 使用者标识符(用户 ID) 00000000-0000-0000-0000-000000000001
claims.email 用户电子邮件地址 user@contoso.com
claims.role 用户角色(如果由标识提供者提供) admin

表达式运算符

Operator 例子 说明
.eq() claims.sub.eq(item.userId) 相等性检查

逻辑运算符

将表达式与 .and().or() 组合:

// User must own the item AND item must be active
@role('authenticated', 'read', {
  policy: (claims, item) =>
    claims.sub.eq(item.userId).and(item.isActive.eq(true))
})

// User is admin OR user owns the item
@role('authenticated', ['update', 'delete'], {
  policy: (claims, item) =>
    claims.role.eq('admin').or(claims.sub.eq(item.ownerId))
})

为了确保正确分组,两边都会自动加上括号。

字段级权限

在角色选项中,使用 includeexclude 指定角色可以访问的字段。

包含特定字段

仅允许在创建操作期间使用 title 字段:

@entity()
@role('authenticated', 'create', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  include: ['title'],
})
export class Document {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) content?: string;
  @text() createdBy!: string;
}

排除特定字段

隐藏读取操作中的敏感字段:

@entity()
@role('authenticated', 'read', {
  exclude: ['lastLogin', 'passwordHash'],
})
export class User {
  @uuid() id!: string;
  @text() email!: string;
  @date({ optional: true }) lastLogin?: Date;
  @text() passwordHash!: string;
}

注释

字段数组类型化为实体的实际属性名称。 重命名某个字段会在每个引用该字段的 includeexclude 列表中产生编译时错误。

操作特定权限

使用多个 @role 修饰器为每个操作应用不同的规则:

@entity()
@role('authenticated', 'create', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  include: ['title', 'content'],
})
@role('authenticated', 'read', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
})
@role('authenticated', 'update', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
  exclude: ['adminNotes'],
})
@role('authenticated', 'delete', {
  policy: (claims, item) => claims.sub.eq(item.createdBy),
})
export class SecureDocument {
  @uuid() id!: string;
  @text() title!: string;
  @text({ optional: true }) content?: string;
  @text({ optional: true }) adminNotes?: string;
  @text() createdBy!: string;
}

此配置:

  • 创建:只有创建者可以创建,并且只允许 titlecontent 字段。
  • 读取:只有创建者可以读取自己的文档。
  • 更新:只有创建者才能更新,但不能修改 adminNotes
  • 删除:只有创建者才能删除。

权限的工作原理

  • 元数据集合@role 修饰器在定义类时收集权限元数据。
  • 架构生成:运行 db apply时,CLI 读取元数据并生成权限配置。
  • 策略编译:TypeScript 策略回调编译为数据访问策略表达式(例如, @claims.sub eq @item.userId)。
  • 运行时强制实施:数据访问层对每个 API 请求强制实施权限。
  • 冲突检测:同一类上的多个 @role 修饰器按角色聚合,并显示冲突声明的警告。

常见模式

仅所有者访问权限

@entity()
@role('authenticated', '*', {
  policy: (claims, item) => claims.sub.eq(item.ownerId)
})
export class PrivateNote {
  @uuid() id!: string;
  @text() ownerId!: string;
  @text() content!: string;
}

已验证用户的完全访问权限

@entity()
@role('authenticated', '*')
export class BlogPost {
  @uuid() id!: string;
  @text() title!: string;
  @text() content!: string;
}

管理员替代

@entity()
@role('authenticated', ['create', 'read', 'update'], {
  policy: (claims, item) =>
    claims.role.eq('admin').or(claims.sub.eq(item.ownerId))
})
@role('authenticated', 'delete', {
  policy: (claims, _item) => claims.role.eq('admin')
})
export class ManagedResource {
  @uuid() id!: string;
  @text() ownerId!: string;
  @text() name!: string;
}

管理员可以修改任何资源,但只有管理员可以删除。

后续步骤