gpt4 book ai didi

java - 如何以及在何处验证 Spring Boot Rest 删除端点中资源的所有权

转载 作者:行者123 更新时间:2023-12-02 09:15:50 25 4
gpt4 key购买 nike

我正在编写简单的 Spring Boot Rest 应用程序,在创建 DELETE 端点时遇到了一些架构问题。我已经尝试过解决这个问题的方法,我需要建议哪个更好以及为什么。

首先,我有一个用 @ControllerAdvice 注释的类,其中包含异常处理程序:

@ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}

@ExceptionHandler(ResourceNotAccessException.class)
public ResponseEntity<String> handleResourceNotAccessException(ResourceNotAccessException ex) {
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(ex.getMessage());
}

所以我想创建端点:

@DeleteMapping("/{offerId}")
public void deleteOffer(@PathVariable Long offerId, Authentication authentication) {
// here code to delete offer.
}

因此,主要假设是登录用户可以删除 id 等于 offeId 的优惠,并且他是该优惠的所有者(我可以使用代码检查所有权: offer.getOwner().getUsername().equals( authentication.getName()); ) 或具有 Role.ADMIN

我想出了三种方法来做到这一点:

第一

所有逻辑都在 Controller 中,只有简单的方法

@DeleteMapping("/{offerId}")
public void deleteOffer(@PathVariable Long offerId, Authentication authentication) {
AbstractOffer offer = offerService.findOfferById(offerId).orElseThrow(() -> new ResourceNotFoundException("Offer with given Id doesn't exists."));
if (!offerService.isOwner(offer,authentication)) throw new ResourceNotAccessException("Cannot delete offer if you aren't owner");
offerService.deleteOffer(offer);
}

第二个

删除此资源的逻辑放入PermissionEvaluator或使用SPeL和注释进行验证@PreAuthorize(...)在这种情况下,我不会抛出任何异常,并且有不称为ControllerAdvice

`

第三

将所有逻辑放入服务类中, Controller 仅调用此方法。

@Service
public class OfferService {
.
.
.
public void deleteOfferIfOwner(Long offerId,Authentication authentication) {
AbstractOffer offer = findOfferById(offerId).orElseThrow(() -> new ResourceNotFoundException("Offer with given Id doesn't exists."));
if(!isOwner(offer,authentication)) throw new ResourceNotAccessException("Cannot delete offer if you aren't owner");
deleteOffer(offer);
}
.
.
}

然后:

@DeleteMapping("/{offerId}")
public void deleteOffer(@PathVariable Long offerId, Authentication authentication) {
offerService.deleteOfferIfOwner(offerId,authentication);
}

摘要

在我看来,第一个解决方案很好,因为异常连接到 Controller 并转换为响应,但我不确定应该在 Controller 中放入多少逻辑以及在这种情况下如何验证身份验证是否为管理员。

第二个 - 不知道该说什么,对我来说太复杂了,我必须将服务类注入(inject)验证类或注释(不确定这是否是好的做法),但它应该可以工作。

第三个 - 我将所有逻辑放入服务中并抛出 2 个异常,它们是运行时异常并连接到 Controller 和controllerAdvice,因为这些异常取决于调用端点的结果。

我很高兴获得一些如何正确解决此问题的提示以及有关代码和项目架构的任何反馈。当前的大部分代码都可以在这里找到: Github

最佳答案

我在应用程序中处理此问题的方式类似于您的第一种和第三种方式,但有一点不同。

更具体地说,我使用一个定制的类ResourceAccessHelper,并将所有代码放入其中,以验证用户对资源的访问权限,或者在用户不满足所需条件时抛出异常。

/**
* Helper class for checking whether a user has certain access rights to a resource during an HTTP
* request.
*
* @author Thanos Psaridis
*/
@Component
public class ResourceAccessHelper {

private final RolesHelper rolesHelper;

/**
* Constructor accepting {@link RolesHelper}
*
* @param rolesHelper Helper class for checking user Roles
*/
@Autowired
public ResourceAccessHelper(RolesHelper rolesHelper) {
this.rolesHelper = rolesHelper;
}

/**
* Getter for {@link RolesHelper}
*
* @return {@link RolesHelper}
*/
public RolesHelper getRolesHelper() {
return rolesHelper;
}

/**
* Verifies that the given parameters are true by Checking user access by course id
*
* @param userEmail user email
* @param courseId the course id the user may have access to
* @throws AccessDeniedException in case user does not have access to this course id
*/
public final void checkAccess(@NotNull final String userEmail, long courseId)
throws AccessDeniedException {
checkCourseDaoAccess(userEmail, courseId);
}
/**
* Custom logic for checking whether a user can pass certain constraints given a courseId.
*
* @param userEmail user email
* @param courseId the course id the user may have access to
* @throws AccessDeniedException in case the access requirements are not met.
*/
private void checkCourseDaoAccess(@NotNull String userEmail, long courseId)
throws AccessDeniedException {
if (isSuperAdmin(userEmail)) return;

final AppUser appUser = rolesHelper.getUser(userEmail);

if (isAdmin(userEmail)) {
final List<Course> tenantCourses = appUser.getTenant().getCourses();

final Optional<Course> optional =
tenantCourses.stream().filter(course -> course.getId().equals(courseId)).findAny();

if (optional.isPresent()) return;
}

if (isStudent(userEmail)
&& appUser
.getEnrolments()
.stream()
.anyMatch(enrolment -> enrolment.getCourse().getId().equals(courseId))) return;
throw new AccessDeniedException("ACCESS DENIED");
}
}

然后我会调用上面的类,然后调用您的服务类,该服务类通过将资源包装在 try-catch block 中来删除 Controller 中的资源,如下所示。

 try {
// check access beforehand
resourceAccessHelper.checkAccess(principalObject.getEmail(), givenCourseId);
//proceed with the deletion of course.
courseService.deleteCourse(givenCourseId)
} catch (AccessDeniedException ignored) {
//handle your exception here by sending a 404 response error or something similar
}

关于java - 如何以及在何处验证 Spring Boot Rest 删除端点中资源的所有权,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43856641/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com