Опубликовать авторизацию Spring асинхронного ответа контроллера

У меня есть REST-контроллер с методом GET. Возвращает ресурс. Я хочу проверить, принадлежит ли ресурс авторизованному пользователю, сравнив 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 в @PostAuthorize SPEL выражение.

Является ли это возможным? Может быть, кто-то может предложить альтернативный подход? Любые предложения приветствуются.

1 ответ

Решение

Не смог достичь своей цели с 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;
    }
}
Другие вопросы по тегам