检查权限
这页只回答一个问题:代码里到底该用哪个方法判断权限。
日常请求里先用 pc.forSubject(input) 绑定当前用户,然后用 can()、cannot() 或 assert() 判断某个动作能不能执行。后半部分的角色、规则、快照读取,主要用于管理后台展示和排查问题,不是每个业务请求都要调用。
布尔检查与强制执行
const subject = pc.forSubject({
userId: 'u-1',
scope: { tenantId: 'acme' },
});
const allowed = await subject.can('invoke', 'api:GET:/api/orders');
const blocked = await subject.cannot('invoke', 'api:DELETE:/api/orders');
await subject.assert('invoke', 'api:GET:/api/orders');
{ "allowed": true, "blocked": true, "assertResult": "void" }
上面的 JSON 是把三个不同调用整理在一起的教程汇总,不是任何一个方法的原始响应。
can 返回布尔值,cannot 返回精确逻辑取反。允许时 assert 完成且没有返回值,否则抛出 PERMISSION_DENIED。操作被阻止不代表一定存在显式 deny;默认拒绝也会阻止。
如果规则使用 valueFrom: 'context.xxx',不要把 context 传给 subject.can()。应在创建 subject 时绑定:
const subject = pc.forSubject(
{ userId: 'u-1', scope: { tenantId: 'acme' } },
{ orderAmount: 1200 },
);
也可以直接使用 core 级方法:pc.can(subjectInput, action, resource, context)。普通 subject facade 的方法签名始终是 subject.can(action, resource)、subject.assert(action, resource)。
接口检查应使用匹配后的 API 路由模板,例如 api:GET:/orders/:id,不要使用带查询参数的具体 URL。授权和检查时必须保持 action 与 resource 命名一致。
解释一次决策
const explanation = await subject.explain(
'invoke',
'api:DELETE:/api/orders',
);
{
"data": {
"allowed": false,
"action": "invoke",
"resource": "api:DELETE:/api/orders",
"reason": "no-allow",
"evaluations": [
{ "action": "invoke", "allowed": false, "reason": "no-allow" }
]
},
"detailBudget": { "limit": 100, "returned": 0, "truncated": false, "digest": "..." }
}
这是 explain() 的原始 SubjectRuntimeResult<PermissionExplanation>。data 是决策解释,detailBudget 说明有界明细是否完整;它不是 can() 的返回结构。
常见原因包括 allow、explicit-deny、no-allow、policy-unknown、role-disabled 和 context-missing。解释轨迹是有界响应;在认定全部来源都已返回前,应检查 detailBudget。
排查:读取角色及其规则
const scoped = pc.scope({ tenantId: 'acme' });
const role = await scoped.roles.get('order-reader');
const own = await scoped.roles.getOwnRules('order-reader');
const effective = await scoped.roles.getEffectiveRules('order-reader');
const chain = await scoped.roles.getChain('order-reader');
{
"role": { "id": "order-reader", "parentId": null, "revision": 2 },
"ownRules": [
{ "effect": "allow", "action": "invoke", "resource": "api:GET:/api/orders" }
],
"effectiveRuleCount": 1,
"chain": [{ "role": { "id": "order-reader" }, "depth": 0, "included": true }]
}
该 JSON 是从四个原始 envelope 中提取字段后的教程汇总。四个方法不会共同返回这个对象。
getOwnRules 只返回直接挂在这个角色上的规则。getEffectiveRules 还包含继承规则、冲突、来源角色 ID 和菜单生成来源。getChain 说明单父角色链上的每个角色为何被包含或排除。
排查:读取并替换用户角色
await scoped.userRoles.assign('u-1', 'order-reader');
await scoped.userRoles.assign('u-1', 'operator');
const direct = await scoped.userRoles.getDirect('u-1');
const saved = await scoped.userRoles.set('u-1', ['order-reader'], {
expectedRevision: direct.data.revision,
});
const effectiveRoles = await scoped.userRoles.getEffective('u-1');
{
"beforeSet": ["operator", "order-reader"],
"afterSet": ["order-reader"],
"effective": ["order-reader"]
}
该 JSON 同样是教程汇总。assign() 和 set() 各自返回 mutation envelope,getDirect/getEffective 各自返回 read envelope。
assign 是增量添加,角色已经绑定时保持幂等。set 是受 expectedRevision 保护的全量替换;列表中缺少的角色会被撤销。管理后台保存完整角色勾选结果时使用 set,单个复选框事件不要直接做全量替换。
排查:读取用户权限快照
const permissions = await subject.getPermissions();
const invokeResources = await subject.getResources('invoke');
{
"permissions": {
"data": {
"subject": { "userId": "u-1", "scope": { "tenantId": "acme" } },
"directRoleIds": ["order-reader"],
"roles": {
"total": 1,
"items": [{
"role": { "id": "order-reader", "status": "enabled", "parentId": null },
"direct": true,
"viaRoleIds": ["order-reader"],
"depth": 0,
"included": true
}],
"truncated": false,
"digest": "..."
},
"rules": {
"total": 1,
"items": [{
"effect": "allow",
"action": "invoke",
"resource": "api:GET:/api/orders",
"sourceRoleId": "order-reader",
"inherited": false,
"depth": 0
}],
"truncated": false,
"digest": "..."
},
"conflicts": { "total": 0, "items": [], "truncated": false, "digest": "..." }
},
"detailBudget": { "limit": 100, "returned": 2, "truncated": false, "digest": "..." }
},
"invokeResources": {
"data": [{
"action": "invoke",
"resource": "api:GET:/api/orders",
"conditional": false,
"sourceRoleIds": {
"total": 1,
"items": ["order-reader"],
"truncated": false,
"digest": "..."
}
}],
"detailBudget": { "limit": 100, "returned": 1, "truncated": false, "digest": "..." }
}
}
外层 permissions/invokeResources 是教程为并列展示而组装的对象;两项内部保留了各自的原始 subject runtime response。
detailBudget 是这些诊断方法返回值的一部分,不是 subject facade 的入参。getPermissions() 返回直接角色 ID、有界的有效角色、有效规则和冲突。getResources(action?) 返回有效资源模式,并标记带条件的条目。这些方法是诊断快照,不能替代具体操作鉴权;实际请求仍应使用已经绑定策略上下文的 subject 调用 can 或 assert。
下一步继续看数据权限。继承行为请继续阅读角色继承。精确签名见核心与上下文、角色 API和用户角色 API。