【问题标题】:How to fetch records in JPA repository using where clause injection?如何使用 where 子句注入获取 JPA 存储库中的记录?
【发布时间】:2021-10-30 08:56:19
【问题描述】:

我有一个要求,用户应该能够根据页面索引和页面大小搜索数据。用户还可以以 sql 查询格式传递过滤器和排序条件(例如过滤器查询 name='abc' and enabled =true 和 order query name asc, lastname desc)。

我应该如何使用 JPA Repository 并基于上述选择标准来实现这一目标?

以下将是搜索记录的确切请求格式。

{
  "page_index": 1,
  "page_size": 100,
  "search_string": "name = 'abc' and enabled = true",
  "sort_criteria": "name DESC, lastname ASC"
}

【问题讨论】:

  • 嗨,你问的太多了,你提供的关于你的项目、代码和设置的信息太少了。您可以在此处阅读 jpa 和 spring-data 中的分页:baeldung.com/jpa-pagination 和此处:baeldung.com/spring-data-jpa-pagination-sorting。尝试基于这些的东西,如果可能的话,返回一个更强大的问题,分享你的一些代码。
  • P.S.您的search_stringsort_criteria 示例表明您应该使用entityManager.createQuery 来构建查询,而不是依赖JpaRepository 查询方式(方法名称和/或@Query 注释)

标签: spring-boot jpa spring-data-jpa


【解决方案1】:

这里有一些东西可以帮助你开始:

Foo 实体(请注意,我使用 lombok 进行 @Data@ToString 等注释以避免样板代码)

@Entity
@Table(name = "foo")
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
public class Foo {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "name", unique = true)
    private String name;

    @Column(name = "lastname", unique = true)
    private String lastname;

    @Column(name = "enabled")
    private boolean enabled;
}

CustomFooRepository 包含我们用于查询的自定义方法的接口

public interface CustomFooRepository {

    List<Foo> customQuery(Integer pageIndex, Integer pageSize, String whereClause, String sorting);
}

CustomFooRepositoryImpl 实现CustomFooRepository。在这里,我们注入EntityManager 来按照我们的意愿组成我们的原生查询:

public class CustomFooRepositoryImpl implements CustomFooRepository {

    @Autowired
    EntityManager em;

    @Override
    public List<Foo> customQuery(Integer pageIndex, Integer pageSize, String whereClause, String sorting) {
        String sql = "SELECT * FROM foo";
        if (whereClause != null) {
            sql = sql + " WHERE " + whereClause; // prone to SQL Injection beware!!!
        }
        if (sorting != null) {
            sql = sql + " ORDER BY " + sorting; // prone to SQL Injection beware!!!
        }
        Query nativeQuery = em.createNativeQuery(sql, Foo.class);
        nativeQuery.setFirstResult((pageIndex-1) * pageSize);
        nativeQuery.setMaxResults(pageSize);
        return nativeQuery.getResultList();
    }
}

我们的 FooRepository 扩展了 JpaRepositoryCustomFooRepository

public interface FooRepository extends JpaRepository<Foo, Long>, CustomFooRepository {
}

最后进行一些测试来证明它有效:

@SpringBootTest
public class FooTests {

    @Autowired
    FooRepository fooRepository;

    @Test
    void testFoo() {
        for(int i = 0; i < 100; i++) {
            fooRepository.save(new Foo(null, "name" + (i+1), "lastname" + (i+1), i%2==0));
        }

        List<Foo> all = fooRepository.findAll();
        System.out.println("All Foos in DB");
        all.forEach(System.out::println);
        System.out.println("-------------------------------");


        List<Foo> foos = fooRepository.customQuery(1, 10, "name like '%name%' and enabled = true", "name DESC, lastname ASC");
        System.out.println("First 10 results (page 1 and pageSize 10) of Foos in DB with name like '%name%' and enabled = true and sorted by name DESC, lastname ASC");
        foos.forEach(System.out::println);

    }
}

测试的输出是:

All Foos in DB
Foo(id=1, name=name1, lastname=lastname1, enabled=true)
Foo(id=2, name=name2, lastname=lastname2, enabled=false)
Foo(id=3, name=name3, lastname=lastname3, enabled=true)
Foo(id=4, name=name4, lastname=lastname4, enabled=false)
Foo(id=5, name=name5, lastname=lastname5, enabled=true)
Foo(id=6, name=name6, lastname=lastname6, enabled=false)
Foo(id=7, name=name7, lastname=lastname7, enabled=true)
Foo(id=8, name=name8, lastname=lastname8, enabled=false)
Foo(id=9, name=name9, lastname=lastname9, enabled=true)
Foo(id=10, name=name10, lastname=lastname10, enabled=false)
Foo(id=11, name=name11, lastname=lastname11, enabled=true)
Foo(id=12, name=name12, lastname=lastname12, enabled=false)
Foo(id=13, name=name13, lastname=lastname13, enabled=true)
Foo(id=14, name=name14, lastname=lastname14, enabled=false)
Foo(id=15, name=name15, lastname=lastname15, enabled=true)
Foo(id=16, name=name16, lastname=lastname16, enabled=false)
Foo(id=17, name=name17, lastname=lastname17, enabled=true)
Foo(id=18, name=name18, lastname=lastname18, enabled=false)
Foo(id=19, name=name19, lastname=lastname19, enabled=true)
Foo(id=20, name=name20, lastname=lastname20, enabled=false)
....
Foo(id=90, name=name90, lastname=lastname90, enabled=false)
Foo(id=91, name=name91, lastname=lastname91, enabled=true)
Foo(id=92, name=name92, lastname=lastname92, enabled=false)
Foo(id=93, name=name93, lastname=lastname93, enabled=true)
Foo(id=94, name=name94, lastname=lastname94, enabled=false)
Foo(id=95, name=name95, lastname=lastname95, enabled=true)
Foo(id=96, name=name96, lastname=lastname96, enabled=false)
Foo(id=97, name=name97, lastname=lastname97, enabled=true)
Foo(id=98, name=name98, lastname=lastname98, enabled=false)
Foo(id=99, name=name99, lastname=lastname99, enabled=true)
Foo(id=100, name=name100, lastname=lastname100, enabled=false)

First 10 results (page 1 and pageSize 10) of Foos in DB with name like '%name%' and enabled = true and sorted by name DESC, lastname ASC
Foo(id=99, name=name99, lastname=lastname99, enabled=true)
Foo(id=97, name=name97, lastname=lastname97, enabled=true)
Foo(id=95, name=name95, lastname=lastname95, enabled=true)
Foo(id=93, name=name93, lastname=lastname93, enabled=true)
Foo(id=91, name=name91, lastname=lastname91, enabled=true)
Foo(id=9, name=name9, lastname=lastname9, enabled=true)
Foo(id=89, name=name89, lastname=lastname89, enabled=true)
Foo(id=87, name=name87, lastname=lastname87, enabled=true)
Foo(id=85, name=name85, lastname=lastname85, enabled=true)
Foo(id=83, name=name83, lastname=lastname83, enabled=true)

您可以选择此示例代码并根据您的需要进行调整。

请注意:

whereClausesorting 字符串直接放在SQL 查询中很容易出现SQL injection,恶意用户可能会利用它来泄露和/或破坏数据库中的数据。您真的应该尝试使用query#setParameter 方法来绑定where 和sort 参数,而不是将它们附加到SQL 字符串中。您可以在我提供的有关 SQL 注入的链接中阅读更多信息。我的示例代码很容易受到 SQL 注入的影响,但为了简单起见,我选择了它。

【讨论】:

  • 感谢您的快速回复。它完全有效,我想要它。正如您在此处指出的那样,此实现容易发生 SQL 注入,我同意您的看法。但是,我通过在排序条件中添加删除/删除查询(“name DESC,lastname ASC; Delete from foo;”)尝试了以下几种情况,并且由于行 nativeQuery.setMaxResults(pageSize )。是否有任何我应该尝试进行 sql 注入的场景?
猜你喜欢
  • 2015-05-18
  • 1970-01-01
  • 2017-10-14
  • 2016-10-11
  • 2013-05-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-06-25
相关资源
最近更新 更多