发布授权Spring异步控制器响应

时间:2017-07-20 21:39:39

标签: spring asynchronous spring-security spring-web deferred-result

我有一个带有GET方法的REST控制器。它返回一个资源。我想通过将owner上的Resource字段与授权用户的登录信息进行比较来验证资源是否属于授权用户。通过正常的同步请求,我可以这样做:

@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类是一个简单的DTO:

public class Resource {

    private String ownerLogin;

    // other fields, getters, setters

}

在第二个示例中,Spring Security在ownerLogin实例上搜索DeferredResult字段。我希望将异步解析的Resource视为returnObject SPEL表达式中的@PostAuthorize

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

1 个答案:

答案 0 :(得分:0)

无法通过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所有者和请求用户的登录名不匹配,则抛出一个解析为@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; } } HTTP状态的异常:

from fractions import Fraction

>>> print("Fraction (0.5):", Fraction(0.5))
Fraction (0.5): 1/2
>>> print("Fraction (0.1):", Fraction(0.1)) 
Fraction (0.1): 3602879701896397/36028797018963968
>>> print(1/10) 
0.1