Опубликовать авторизацию 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;
}
}