spring-data-jpa 为通过事件更新项状态设计安全高效的API

gzszwxb4  于 2022-11-10  发布在  Spring
关注(0)|答案(1)|浏览(138)

最近我一直在做一个简单的状态跟踪系统,它的主要目的是持久化更新,定期从移动的客户端发送到关系数据库中,以便进一步分析/呈现。
移动的客户端使用AAD发布的JWT来验证我们的API。我需要找到一种方法来验证用户是否有权发送某个项目的更新(目前只有其创建者可以这样做)。
我们假设这些更新可以由许多客户端以很小的间隔(15-30秒)发送。我们将只有一个项目处于活动状态的每个用户。
后端应用程序基于Spring-Boot,使用带有MS AAD启动器和Spring Data JPA的Spring Security。
显然,我们只需执行以下操作:
1.用户_1创建项目_1

  1. User_1发送Item_1的更新
    Item有一个owner_ID字段,在插入Update之前,我们只需检查Item_1.owner_ID是否=User_1.ID -这意味着我们需要在每次插入之前获取原始Item。
    我想知道是否有更优雅的方法来解决这类问题。我们是否应该使用某种缓存解决方案来保存允许的ID对,例如{User_1,Item_1}?
zvokhttg

zvokhttg1#

WHERE子句

您可以将它作为条件包含在WHERE子句中。例如,如果您正在更新记录X,则可能以下列内容开头:

UPDATE table_name SET column1 = value1 WHERE id = X

但是,您可以改为:

UPDATE table_name SET column1 = value1 WHERE id = X AND owner_id = Y

如果所有者不是Y,那么这个值就不会被更新。

@Query("UPDATE table_name SET column1 = ?value1 WHERE id = ?id AND owner_id = ?#{principal.ownerId}")
public int updateValueById(String value1, String id);

其中,principal是从Authentication#getPrincipal返回的值。

缓存

从技术上讲,缓存可以阻止第一次数据库调用,这是正确的,但它会引入其他复杂性。保持缓存新鲜就足够了,只有当引入缓存的复杂性显然会带来所需的、观察到的性能增益时,我才会尝试它。

@PostAuthorize

或者,您可以进行额外的调用,并使用框架来简化样板文件。例如,您可以在控制器中使用@PostAuthorize注解,如下所示:

@PutMapping("/updatevalue")
@Transactional
@PostAuthorize("returnObject?.ownerId == authentication.principal.ownerId")
public MyWidget update(String value1, String id) {
    MyWidget widget = this.repository.findById(id);
    widget.setColumn1(value1);
    return widget;
}

通过这种安排,Spring Security将根据登录的用户检查返回值的ownerId,如果失败,则事务将被回滚,并且更改不会进入数据库。
要使其正常工作,请确保Spring的事务拦截器位于Spring Security的授权后拦截器之前,如下所示:

@EnableMethodSecurity
@EnableTransactionManagement(order=-1)

这个解决方案的缺点是仍然有两个相同的DB调用。我喜欢它,因为它允许框架强制执行授权规则。要了解更多信息,请看一下遵循此模式的this sample application

相关问题