Spring Data Rest 中的存储库访问控制基于用户主体

Repository access control in Spring Data Rest based off user princpal

我正在尝试实施细粒度访问控制,同时仍然利用 Spring 数据剩余。

我正在努力确保 CrudRepository 的安全,以便用户只能修改或插入属于他们的数据。我正在使用 @PreAuthorize/@PostAuthorize@PreFilter/@PostFilter 来锁定对当前主体的访问。

到目前为止我的存储库是这样的。

public interface MyRepository extends CrudRepository<MyObject, Integer> {

    @PreAuthorize("#entity.userId == principal.id")
    @Override
    <S extends MyObject> S save(S entity);

    @PreFilter("filterObject.userId === principal.id")
    @Override
    <S extends MyObject> Iterable<S> save(Iterable<S> entities);

    @PostAuthorize("returnObject.userId == principal.id")
    @Override
    MyObject findOne(Integer integer);

    @PostFilter("filterObject.userId == principal.id")
    @Override
    Iterable<MyObject> findAll();

}

虽然这有点乏味,但它似乎确实完成了我所追求的。 (如果有人知道更好的方法,请随时告诉我!)

我 运行 遇到问题的地方是 delete()count()exists()

    @Override
    long count();

    @Override
    void delete(Integer integer);

    @Override
    void delete(MyObject entity);

    @Override
    void deleteAll();

    @Override
    boolean exists(Integer integer);

这些方法要么采用 Integer ID 参数,要么完全采用 none。看来我必须首先 select 具有输入 ID 的实体,然后执行身份验证检查。

在存储库中是否可以进行这种类型的授权?

谢谢

编辑:

多亏了 ksokol,这似乎可以正常工作了。

我在 @Configuration class

中添加了一个新 bean
@Bean
public EvaluationContextExtension securityExtension() {
    return new SecurityEvaluationContextExtensionImpl();
}

此 bean 扩展 EvaluationContextExtensionSupport 并将 getRootObject 覆盖为 return 一个包含我的自定义主体的 SecurityExpressionRoot

public class SecurityEvaluationContextExtensionImpl extends EvaluationContextExtensionSupport {
@Override
public String getExtensionId() {
    return "security";
}

@Override
public Object getRootObject() {
        Authentication authentication =   SecurityContextHolder.getContext().getAuthentication();
        return new SecurityExpressionRoot(authentication){};
    }
}

Spring Security 4.0 开始,您可以在 Spring Data JPA 查询中访问安全上下文。

SecurityEvaluationContextExtension bean 添加到您的 bean 上下文中:

@Bean
public SecurityEvaluationContextExtension securityEvaluationContextExtension() {
    return new SecurityEvaluationContextExtension();
}

现在您应该可以在 Spring 数据查询中访问 Principal

@Query("select count(m) from MyObject as m where m.user.id = ?#{ principal?.id }")
@Override
long count();

@Modifying
@Query("delete from MyObject as m where m.id = ?1 and m.user.id = ?#{ principal?.id }")
@Override
void delete(Integer integer);

@Modifying
@Query("delete from MyObject as m where m.id = ?1 and m.user.id = ?#{ principal?.id }")
@Override
void delete(MyObject entity);

@Modifying
@Query("delete from MyObject as m where m.user.id = ?#{ principal?.id }")
@Override
void deleteAll();

@Query("select 1 from MyObject as m where m.id = ?1 and m.user.id = ?#{ principal?.id }")
@Override
boolean exists(Integer integer);

注意。查询可能有错误。我没有时间测试它。

也可以通过在自定义 Spring 存储库事件处理程序中实施检查来实现。参见 @HandleBeforeCreate@HandleBeforeUpdate@HandleBeforeDelete

或者,您可以使用基于权限的表达式,例如使用 ACL 或您自定义的,您可以编写 @PreAuthorize("hasPermission(#id, 'MyObject', 'DELETE')").