6.2.5.1. 约束
Constraints - 约束 可以配置 行级访问控制,即能管理对于数据特定行的访问。与影响整个实体类别的权限许可不同,约束至影响特定的实体实例。约束可以设置增删查改(CRUD)操作,所以在加载或者禁用实体实例操作的时候,框架会过滤掉某些符合约束配置的实体。另外,也可以添加自定义约束,不限于 CRUD 操作。
从用户自身的访问组上的约束开始,直到其访问组层级上的所有访问组约束都会对该用户生效。所以,当用户所在访问组树形层级越低,给用户配置的约束会越多。 |
所有客户端层通过标准 DataManager 发起的操作都会触发约束检查。如果实体不满足约束,添加、更改或删除的时候会抛出 RowLevelSecurityException
异常。参阅 数据访问检查 章节了解框架中不同机制如何使用安全约束。
约束有两种类型:在数据库检查约束和内存中检查的约束。
-
对在数据库检查的约束,其条件通过 JPQL 子句设置。设置以后会被追加到查询语句之后,这样不满足条件的结果在数据库级别会被过滤掉。数据库检查的约束只能用到查询操作中,并且只影响加载的对象关系图中的根节点实体。
-
对在内存检查的约束,其条件通过 Java 代码(如果约束是设计时定义)或 Groovy 表达式(如果约束是运行时定义)设置。这类表达式执行在对象图中的每个实体上,当不满足条件时,数据会被从对象图中过滤掉。
- 设计时定义约束
-
约束可以在一个继承了
AnnotatedAccessGroupDefinition
的类中定义,那个类用于定义 访问组。继承类必须存于core
模块。下面是一个访问组的示例,为Customer
和Order
实体定义了几个约束:@AccessGroup(name = "Sales", parent = RootGroup.class) public class SalesGroup extends AnnotatedAccessGroupDefinition { @JpqlConstraint(target = Customer.class, where = "{E}.grade = 'B'") (1) @JpqlConstraint(target = Order.class, where = "{E}.customer.grade = 'B'") (2) @Override public ConstraintsContainer accessConstraints() { return super.accessConstraints(); } @Constraint(operations = {EntityOp.CREATE, EntityOp.READ, EntityOp.UPDATE, EntityOp.DELETE}) (3) public boolean customerConstraints(Customer customer) { return Grade.BRONZE.equals(customer.getGrade()); } @Constraint(operations = {EntityOp.CREATE, EntityOp.READ, EntityOp.UPDATE, EntityOp.DELETE}) (4) public boolean orderConstraints(Order order) { return order.getCustomer() != null && Grade.BRONZE.equals(order.getCustomer().getGrade()); } @Constraint(operations = {EntityOp.UPDATE, EntityOp.DELETE}) (5) public boolean orderUpdateConstraints(Order order) { return order.getAmount().compareTo(new BigDecimal(100)) < 1; } }
1 - 只加载 grade
属性是B
(对应于Grade.BRONZE
枚举值) 的 customer。2 - 只加载 grade
属性是B
的用户的 orders。3 - 内存约束,从加载的对象图中过滤掉 grade
属性不是Grade.BRONZE
的 customer。4 - 内存约束,允许使用 grade == Grade.BRONZE
的 customer 的 orders。5 - 内存约束,允许修改或删除 amount < 100
的 orders。编写 JPQL 约束是需要遵从以下规则:
-
{E}
需要作为被加载实体的别名,当执行查询语句时,它会被查询语句中真正使用的别名替代。 -
以下预定义常量可以用作 JPQL 参数:
-
where
属性中的内容会被添加到where
子句并用and
连接。不需要显式添加where
单词,系统会自动添加。 -
join
属性中的内容会被添加到from
子句,该字段需要用逗号“,”、join
或left join
开头。
-
- 运行时定义约束
-
如需创建约束,打开 Access Groups - 访问组 界面,选择一个需要创建约束的组,然后切换到 Constraints - 约束 标签页。约束编辑界面带有 Constraint Wizard - 约束向导,能帮助用实体属性创建简单的 JPQL 和 Groovy 表达式。当选择 Custom - 自定义 作为操作类型时,需要填写 Code - 代码 字段,设置一个用于标记该约束的代码。
Join Clause 和 Where Clause 字段内的 JPQL 编辑器支持实体名称和属性的自动完成功能。如需调用自动完成功能,按下 Ctrl+Space。如果是在输入“.”之后出现的智能提示,则会显示符合当前上下文的实体属性列表,否则显示所有数据模型的实体。
Groovy 内存约束中,使用
{E}
占位符变量表示被检查实体实例。另外,userSession
作为UserSession
类型的变量也会传递给脚本。下面的例子展示的约束用来检查实体是由当前用户创建的:{E}.createdBy == userSession.user.login
当违反约束时,会给用户展示一个通知消息。每个约束的通知消息标题和内容都可以做本地化:使用 Access Groups - 访问组 界面 Constraints - 约束 标签页的 Localization - 本地化 按钮。
- Checking constraints in application code
-
开发者可以使用以下
Security
接口检查某一实体的约束条件:-
isPermitted(Entity, ConstraintOperationType)
- 根据操作类型检查是否约束。 -
isPermitted(Entity, String)
- 根据输入字符串检查自定义约束。
也可以基于
ItemTrackingAction
连接 action 和特定约束。在action
的 XML 节点中设置constraintOperationType
属性或者使用setConstraintOperationType()
方法设置。注意,约束的代码会在客户端层执行,所以不能使用中间层的类。示例:
<table> ... <actions> <action id="create"/> <action id="edit" constraintOperationType="update"/> <action id="remove" constraintOperationType="delete"/> </actions> </table>
-