Spring 引导和 JPA 存储库 -- 如何按 ID 过滤 GET
Spring Boot and JPA Repository -- how to filter a GET by ID
我正在重写一个应用程序,这次使用来自 Spring 的 RESTful 接口。我假设服务器端授权是最好的。即:
假设用户 1 使用此 REST 存储库。 He/she 访问 mysite.com/heroes/1 并从英雄 table.
获取 (id = 1) 英雄
用户 2 无权查看 (id = 1) 英雄,但无论如何都可以制作一个 cURL 语句来尝试。我声称服务器应该阻止用户 2 访问 (id = 1) 英雄。
我相信服务器可以提取一个 JWT 负载,它会给我用户名或密码(我把它放在那里)。服务器从该有效负载中获取用户的帐户,并知道英雄 he/she 有权查看哪些内容。
我已经通过服务和 DAO 类 实现了这个目标。但是,我看到的 Spring Boot 和 JPA 教程提倡使用 CrudRepository 实现来减少编码。我想知道如何使用这项技术进行过滤。
下面是来自网络的示例:
@RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
}
当访问 mysite.com/heroes/1 时,它会自动 returns 来自 hero (id = 1) 的数据。我想指示它让我选择允许哪些 ID 值。也就是在运行时通过代码给它提供一个查询参数。
作为测试,我提供了以下代码:
@RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
@Query ("from Hero h where id in (1, 3, 5)")
public Hero get();
}
但是,它不会阻止 mysite。com/heroes/2 返回 (id = 2) 英雄。
我怎样才能达到我想要的目标?
谢谢,杰罗姆。
更新 5/13,5:50 下午
我的请求被误解了,所以我进一步说明我的意图。
- 用户1和2是普通用户,正在访问他们的账户。
- 每个用户必须限制在 his/her 自己的帐户。
- 用户不能通过伪造对其他人数据的请求来作弊。
因此,服务器需要从 JWT 令牌中提取用户 ID 等,并将其应用到代码中,使 /heroes 查询起作用。
我的原始示例源自 this tutorial。其中唯一的 Java 类 是 Hero 和 HeroRepository。 DAO、服务或控制器没有明确的 类。包含的 Spring 库让所有的 /heroes 获取都可以在没有进一步编码的情况下发生。
再次感谢您的关注和帮助。杰罗姆。
控制器使用此代码:-
@RestController
@RequestMapping("/cities")
public class CityController {
private static final Logger logger = LoggerFactory.getLogger(CityController.class);
@Autowired
private CityService cityService;
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public RestResponse find(@PathVariable("id") Long id) {
.
.
}
使用以下代码进行 Repo :-
public interface CityRepo extends JpaRepository<FCity, Long> {
@Query("select e from FCity e where e.cityId = :id")
FCity findOne(@Param("id") Long id);
}
使用以下代码进行服务:-
@Service
@Transactional
public class CityService {
@Autowired(required = true)
private CityRepo cityRepo;
public FCity findOne(Long id) {
return cityRepo.findOne(id);
}
}
我已经创建了 HeroRepository 来解决我理解的所有查询。
I'd like to instruct it to let me choose which ID values to permit.
你可以使用同样的方法实现。
List<Hero> findByIdIn(List<Long> ids);
或者,如果您更喜欢 Query
@Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(@Param("ids") List<Long> ids);
it doesn't block mysite.com/heroes/2 from returning the (id = 2) hero.
我看不到您的 Controller/Service 方法,因此我假设正在调用 findOne()
。您可以使用 ..
来阻止它
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
或者,如果您想更好地控制方法调用,您还可以使用 spring-security
中的 @PreAuthorize
。
// Authorization based method call
@PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
总结
public interface HeroRepository extends CrudRepository<Hero, Long> {
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
// If u want to pass ids as a list
List<Hero> findByIdIn(List<Long> ids);
// Alternative to above one
@Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(@Param("ids") List<Long> ids);
// Authorization based method call
@PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
}
PS:请注意,我从方法返回 Optional<Hero>
。如果查询没有结果,将返回 Optional.empty()
。这将迫使我们在执行任何操作之前检查该值是否存在,从而避免 NullPointerException
.
您可以创建自定义 @Query
,它使用登录用户的信息(此处:id
)。使用此解决方案,用户只能访问与他具有相同 id
的实体。
@Override
@Query("SELECT h FROM Hero h WHERE h.id=?1 AND h.id=?#{principal.id}")
public Hero findOne(Long id);
您需要为 @Query
(link) and create an custom UserDetailsService
(link) 启用包含用户 ID 的自定义 UserDetails 的 SpEl,因此您可以 principal.id
.
您应该以同样的方式保护 findAll()
方法。
我正在重写一个应用程序,这次使用来自 Spring 的 RESTful 接口。我假设服务器端授权是最好的。即:
假设用户 1 使用此 REST 存储库。 He/she 访问 mysite.com/heroes/1 并从英雄 table.
获取 (id = 1) 英雄
用户 2 无权查看 (id = 1) 英雄,但无论如何都可以制作一个 cURL 语句来尝试。我声称服务器应该阻止用户 2 访问 (id = 1) 英雄。
我相信服务器可以提取一个 JWT 负载,它会给我用户名或密码(我把它放在那里)。服务器从该有效负载中获取用户的帐户,并知道英雄 he/she 有权查看哪些内容。
我已经通过服务和 DAO 类 实现了这个目标。但是,我看到的 Spring Boot 和 JPA 教程提倡使用 CrudRepository 实现来减少编码。我想知道如何使用这项技术进行过滤。
下面是来自网络的示例:
@RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
}
当访问 mysite.com/heroes/1 时,它会自动 returns 来自 hero (id = 1) 的数据。我想指示它让我选择允许哪些 ID 值。也就是在运行时通过代码给它提供一个查询参数。
作为测试,我提供了以下代码:
@RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
@Query ("from Hero h where id in (1, 3, 5)")
public Hero get();
}
但是,它不会阻止 mysite。com/heroes/2 返回 (id = 2) 英雄。
我怎样才能达到我想要的目标?
谢谢,杰罗姆。
更新 5/13,5:50 下午
我的请求被误解了,所以我进一步说明我的意图。
- 用户1和2是普通用户,正在访问他们的账户。
- 每个用户必须限制在 his/her 自己的帐户。
- 用户不能通过伪造对其他人数据的请求来作弊。
因此,服务器需要从 JWT 令牌中提取用户 ID 等,并将其应用到代码中,使 /heroes 查询起作用。
我的原始示例源自 this tutorial。其中唯一的 Java 类 是 Hero 和 HeroRepository。 DAO、服务或控制器没有明确的 类。包含的 Spring 库让所有的 /heroes 获取都可以在没有进一步编码的情况下发生。
再次感谢您的关注和帮助。杰罗姆。
控制器使用此代码:-
@RestController
@RequestMapping("/cities")
public class CityController {
private static final Logger logger = LoggerFactory.getLogger(CityController.class);
@Autowired
private CityService cityService;
@RequestMapping(value = "/{id}", method = RequestMethod.GET)
public RestResponse find(@PathVariable("id") Long id) {
.
.
}
使用以下代码进行 Repo :-
public interface CityRepo extends JpaRepository<FCity, Long> {
@Query("select e from FCity e where e.cityId = :id")
FCity findOne(@Param("id") Long id);
}
使用以下代码进行服务:-
@Service
@Transactional
public class CityService {
@Autowired(required = true)
private CityRepo cityRepo;
public FCity findOne(Long id) {
return cityRepo.findOne(id);
}
}
我已经创建了 HeroRepository 来解决我理解的所有查询。
I'd like to instruct it to let me choose which ID values to permit.
你可以使用同样的方法实现。
List<Hero> findByIdIn(List<Long> ids);
或者,如果您更喜欢 Query
@Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(@Param("ids") List<Long> ids);
it doesn't block mysite.com/heroes/2 from returning the (id = 2) hero.
我看不到您的 Controller/Service 方法,因此我假设正在调用 findOne()
。您可以使用 ..
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
或者,如果您想更好地控制方法调用,您还可以使用 spring-security
中的 @PreAuthorize
。
// Authorization based method call
@PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
总结
public interface HeroRepository extends CrudRepository<Hero, Long> {
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
throw new RuntimeException("Forbidden !!");
}
// If u want to pass ids as a list
List<Hero> findByIdIn(List<Long> ids);
// Alternative to above one
@Query("SELECT H FROM Hero H WHERE H.id IN :ids")
List<Hero> alternativeFindByIdIn(@Param("ids") List<Long> ids);
// Authorization based method call
@PreAuthorize("hasRole('ADMIN')")
Optional<Hero> findById(Long id);
}
PS:请注意,我从方法返回 Optional<Hero>
。如果查询没有结果,将返回 Optional.empty()
。这将迫使我们在执行任何操作之前检查该值是否存在,从而避免 NullPointerException
.
您可以创建自定义 @Query
,它使用登录用户的信息(此处:id
)。使用此解决方案,用户只能访问与他具有相同 id
的实体。
@Override
@Query("SELECT h FROM Hero h WHERE h.id=?1 AND h.id=?#{principal.id}")
public Hero findOne(Long id);
您需要为 @Query
(link) and create an custom UserDetailsService
(link) 启用包含用户 ID 的自定义 UserDetails 的 SpEl,因此您可以 principal.id
.
您应该以同样的方式保护 findAll()
方法。