资源角色

资源角色赋予用户对系统中特定对象和操作的权限:对实体的 CRUD 操作权限、实体属性权限、UI 界面权限等。

没有资源角色的用户没有任何权限,无法访问应用程序的数据和 UI。

创建资源角色

可以在设计时使用带注解的 Java 接口(参考 Studio 角色设计器)创建资源角色,或者在运行时用 Administration(管理) → Resource roles(资源角色) 界面创建。

每个角色有一个用户友好的名称和一个编码。给用户分配角色时使用编码,因此如果已经有用户分配了某个角色,不要再次修改此角色的编码了。

定义设计时角色示例:

@ResourceRole( (1)
        name = "Full Access", (2)
        code = "system-full-access") (3)
public interface FullAccessRole {

    // ...
    void fullAccess(); (4)
}
1 @ResourceRole 注解表示这个接口定义了资源角色。
2 用户友好的角色名。
3 角色的编码。
4 接口可以有一个或多个方法用来定义策略注解(见下文)。不同的方法只是用来对相关的策略进行分组。方法名没有限制,当角色在 UI 展示时,方法名作为 Policy group(策略组) 展示。

资源策略

资源角色通过指定 资源策略 定义许可权限。资源策略由一个资源和对该资源的访问权限组成。

实体策略

实体策略指定对实体的 CRUD 操作权限。

在设计时角色中,实体策略使用 @EntityPolicy 注解定义,示例:

@EntityPolicy(
        entityClass = Customer.class,
        actions = {EntityPolicyAction.READ,
                EntityPolicyAction.CREATE,
                EntityPolicyAction.UPDATE})
void customer();

entityClass 参数指定实体。如需定义所有实体的策略,请将 entityName 参数设置为 *

actions 参数设置许可的 CRUD 操作。如需请用所有操作,设置为 EntityPolicyAction.ALL 值。

实体属性策略

实体属性策略指定对实体属性的操作权限。

在设计时角色中,实体属性策略使用 @EntityAttributePolicy 注解定义,示例:

@EntityAttributePolicy(
        entityClass = Customer.class,
        attributes = {"name", "region", "details"},
        action = EntityAttributePolicyAction.MODIFY)
void customer();

entityClass 参数指定实体。如需定义所有实体的策略,请将 entityName 参数设置为 *

attributes 参数指定实体属性名称。如需定义所有实体属性的策略,设置为 *

action 参数设置许可的级别:“view(查看)” 或 “modify(修改)”。“modify” 级别包含 “view”。

界面策略

界面策略指定 UI 界面的许可权限。

在设计时角色中,界面策略使用 @ScreenPolicy 注解定义,示例:

@ScreenPolicy(
        screenIds = {"sample_Customer.browse", "sample_Customer.edit"})
void customer();

screenIds 参数指定许可的 UI 界面 id。如需启用全部界面,设置为 *

菜单策略指定 UI 主菜单项的许可权限。

在设计时角色中,菜单策略使用 @MenuPolicy 注解定义,示例:

@MenuPolicy(
        menuIds = {"sample_Customer.browse"})
void customer();

menuIds 参数指定许可的菜单项 id。如需启用全部菜单项,设置为 *

特殊策略

特殊策略用于定义任何其他功能的权限。

框架使用特定权限来限制对各种机制的访问。例如,通用 REST API 定义了 rest.enabled 策略,因此用户必须拥有此权限才能使用 REST API 接口与应用程序交互。

你也可以使用特定策略来限制对应用程序功能的访问。以下是这种访问控制的示例。

在设计时角色中,特殊策略使用 @SpecificPolicy 注解定义,示例:

@SpecificPolicy(
        resources = {"customer.notify"})
void customer();

使用框架提供的 AccessManagerSpecificOperationAccessContext 类在应用程序代码中检查策略:

@Autowired
private AccessManager accessManager;

public void notifyCustomer(Customer customer) {
    SpecificOperationAccessContext accessContext =
            new SpecificOperationAccessContext("customer.notify");
    accessManager.applyRegisteredConstraints(accessContext);
    if (accessContext.isPermitted()) {
        // do notify
    }
}

参阅 访问约束 部分了解有关检查权限的更多内容。

如需启用所有的特殊策略,在 @SpecificPolicy.resources 注解属性中使用 * 值。

角色粒度

一个资源角色可以有任意数量的策略,并且一个用户可以有任意数量的角色。因此,可以设计具有不同粒度级别的角色:

  • 细粒度的角色定义了对紧密相关资源(如实体、实体 UI 界面和菜单项)的权限。例如,“对 Customer 的完全访问权限”、“可以创建和更新 Orders”。通常会给用户分配几个这样的角色。

  • 粗粒度角色定义了特定工作所需的所有权限,例如,“销售人员”。这样的角色本身可以定义所有权限或从子角色继承,因此可以将粗粒度角色作为细粒度角色的聚合。

我们建议在设计时创建细粒度角色,仅使用运行时功能将它们组合成不同的粗粒度角色,以便能更简单地给用户分配角色,并支持对安全模型临时进行调整。

示例

下面是设计时资源角色的完整示例,启用了对下列实体和它们 UI 的有限访问权限:

resource roles diagram
@ResourceRole(
        name = "Customers: non-confidential info only, cannot delete",
        code = "customer-nonconfidential-access")
public interface CustomerNonConfidentialAccessRole {

    @EntityPolicy(
            entityClass = Customer.class,
            actions = {EntityPolicyAction.READ,
                    EntityPolicyAction.CREATE,
                    EntityPolicyAction.UPDATE})
    @EntityAttributePolicy(
            entityClass = Customer.class,
            attributes = {"name", "region", "details"},
            action = EntityAttributePolicyAction.MODIFY)
    @ScreenPolicy(
            screenIds = {"sample_Customer.browse", "sample_Customer.edit"})
    @MenuPolicy(
            menuIds = {"sample_Customer.browse"})
    void customer();

    @EntityPolicy(
            entityClass = CustomerDetail.class,
            actions = EntityPolicyAction.ALL)
    @EntityAttributePolicy(
            entityClass = CustomerDetail.class,
            attributes = {"content"},
            action = EntityAttributePolicyAction.MODIFY)
    @ScreenPolicy(
            screenIds = {"sample_CustomerDetail.edit"})
    void customerDetail();

    @MenuPolicy(
            menuIds = {"application"})
    void commonMenus();
}