0

我有一个带有 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视为SPEL 表达式returnObject中的。@PostAuthorize

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

4

1 回答 1

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所有者和请求用户的登录名不匹配,则抛出解析为404HTTP 状态的异常:

@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;
    }
}
于 2017-08-28T13:54:45.693 回答