Post 授权 Spring 异步控制器响应

Post authorizing Spring asynchronous controller response

我有一个带有 GET 方法的 REST 控制器。它 returns 一种资源。我想通过比较 Resource 上的 owner 字段与授权用户的登录名来验证资源是否属于授权用户。对于正常的同步请求,我会做这样的事情:

@RestController
@RequestMapping("/api")
public class AController {

    private final AService aService;

    public AController(AService aService) {
        this.aService = aService;
    }

    @GetMapping("/resources/{id}")
    @PostAuthorize("returnObject.ownerLogin == authentication.name")
    public Resource getResource(@PathVariable Long id) {
        return aService.getResource(id);
    }
}

但是如果控制器方法是异步的(用DeferredResult实现)呢?

@RestController
@RequestMapping("/api")
public class AController {

    private final AService aService;

    public AController(AService aService) {
        this.aService = aService;
    }

    @GetMapping("/resources/{id}")
    @PostAuthorize("returnObject.ownerLogin == authentication.name")
    public DeferredResult<Resource> getResource(@PathVariable Long id) {
        DeferredResult<Resource> deferredResult = new DeferredResult<>();

        aService
            .getResourceAsync(id)
            .thenAccept(resource -> {
                deferredResult.setResult(resource);
            });

        return deferredResult;
    }
}

其中 AService 界面如下所示:

@Service
public class AService {

    @Async
    public CompletableFuture<Resource> getResourceAsync(Long id) {
        // implementation...
    }

    public Resource getResource(Long id) {
        // implementation...
    }
}

Resource class是一个简单的DTO:

public class Resource {

    private String ownerLogin;

    // other fields, getters, setters

}

在第二个示例中,Spring 安全性会在 DeferredResult 实例中寻找 ownerLogin 字段。我希望它将异步解析的 Resource 视为 @PostAuthorize SPEL 表达式中的 returnObject

可能吗?也许有人可以建议另一种方法?欢迎提出任何建议。

无法通过 PostAuthorize 实现我的目标,最终做了以下事情:

使 Resource 成为 User 资源的子资源。使用 PreAuthorize 注释来验证用户的登录。

@RestController
@RequestMapping("/api")
public class AController {

    private final AService aService;

    public AController(AService aService) {
        this.aService = aService;
    }

    @GetMapping("/users/{login:" + Constants.LOGIN_REGEX + "}/resources/{id}")
    @PreAuthorize("#login == authentication.name")
    public DeferredResult<Resource> getResource(@PathVariable String login, @PathVariable Long id) {
        DeferredResult<Resource> deferredResult = new DeferredResult<>();

        aService
            .getResourceAsync(login, id)
            .thenAccept(resource -> {
                deferredResult.setResult(resource);
            });

        return deferredResult;
    }
}

AService 中添加了所有权检查。如果 Resource 所有者和请求用户的登录名不匹配,则抛出解析为 404 HTTP 状态的异常:

@Service
public class AService {

    private final ARepository aRepository;

    public AController(ARepository aRepository) {
        this.aRepository = aRepository;
    }

    @Async
    public CompletableFuture<Resource> getResourceAsync(String owner, Long id) {
        Resource resource = aRepository.getResource(id);

        if (!resource.owner.equals(owner)) {
            // resolves to 404 response code
            throw ResourceNotFounException();
        }

        return resource;
    }
}