Spring Boot和JPA Repository - 如何按ID

时间:2017-05-13 16:26:09

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

我正在使用Spring的RESTful接口重写应用程序。我认为服务器端授权是最好的。那就是:

  • Supppose用户1使用此REST存储库。他/她访问mysite.com/heroes/1并从英雄表中获取(id = 1)英雄。

  • 用户2无权查看(id = 1)英雄,但无论如何都可以制作cURL语句。我声称服务器应该阻止用户2访问(id = 1)英雄。

我相信服务器可以提取一个JWT有效负载,它给我用户名或密码(我把它放在那里)。从该有效载荷中,服务器获取用户的帐户并知道他/她有权看到的英雄。

我已经通过服务和DAO类完成了这个目标。但是,我看到Spring Boot和JPA教程使用CrudRepository实现来减少编码。我想知道如何使用这项技术进行过滤。

以下是网络上的一个例子:

@RepositoryRestResource(collectionResourceRel = "heroes", path = "heroes")
public interface HeroRepository extends CrudRepository<Hero, Long> {
}

当访问mysite.com/heroes/1时,它会自动从英雄(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是普通用户,访问他们的帐户。
  • 每个用户必须被限制在他/她自己的帐户中。
  • 用户不能通过制作对其他人的请求而作弊。数据。

因此,服务器需要从JWT令牌中提取用户ID等,并将其应用于代码中,以使/ heroes查询起作用。

我的原始示例源自this tutorial。其中唯一的Java类是Hero和HeroRepository。 DAO,服务或控制器没有明确的类。包含的Spring库允许所有/英雄获取而无需进一步编码。

再次感谢您的所有兴趣和帮助。杰罗姆。

3 个答案:

答案 0 :(得分:0)

我创建了HeroRepository来解决所有问题。

  

我想指示它让我选择允许的ID值。

您可以使用相同的方法实现。

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);
  

它不会阻止mysite.com/heroes/2返回(id = 2)英雄。

我无法看到您的Controller / Service方法,因此我假设正在调用findOne()。您可以使用..

来阻止它
// Disallow everybody to use findOne()
default Hero findOne(Long id) {
    throw new RuntimeException("Forbidden !!");
} 

或者,如果您想要更好地控制方法调用,还可以使用@PreAuthorize中的spring-security

// 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

答案 1 :(得分:0)

您可以创建自定义@Query,使用已登录用户的信息(此处为:id)。使用此解决方案,用户只能访问与他具有相同id的实体。

@Override
@Query("SELECT h FROM Hero h WHERE h.id=?1 AND h.id=?#{principal.id}")
public Hero findOne(Long id);

您需要为@Querylink)启用SpEl并使用自定义UserDetails创建自定义UserDetailsServicelink),其中包含用户的ID,因此你可以做principal.id

以同样的方式保护findAll()方法。

答案 2 :(得分:-4)

将此代码用于Controller: -

@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) {
.
.
}

使用以下代码进行回购: -

 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);
}

}