Возврат 404 вместо 403 при сбое @PostAuthorize

Допустим, у меня есть следующий контроллер. (Предположим, что Order.customer является клиентом, Order.customer принадлежит заказ, и только они должны иметь к нему доступ.)

@RestController
@RequestMapping("/orders")
public class OrderController {
    @GetMapping
    @PostAuthorize("returnObject.customer == authentication.principal")
    public Order getOrderById(long id) {
        /* Look up the order and return it */
    }
}

После просмотра заказа, @PostAuthorize используется, чтобы убедиться, что он принадлежит аутентифицированному клиенту. Если это не так, Spring отвечает 403 Запрещено.

У такой реализации есть проблема: клиенты могут различать несуществующие заказы и заказы, к которым у них нет доступа. В идеале, 404 должны быть возвращены в обоих случаях.

Хотя это можно решить путем внедрения Authentication в метод-обработчик и реализации там собственной логики, есть ли способ добиться этого с помощью @PostAuthorize или аналогичного декларативного API?

Всего 2 ответа


Вы можете попробовать ControllerAdvice, чтобы перехватить и преобразовать AccessDeniedException, которое выбрасывает PostAuthorize.

@RestControllerAdvice
public class ExceptionHandlerController {

    @ResponseStatus(HttpStatus.NOT_FOUND)
    @ExceptionHandler(AccessDeniedException.class)
    public String handleAccessDenied(AccessDeniedException e) {
        return "nothing here"; // or a proper object
    }
}

Вы можете указать пользовательский AccessDeniedHandler в своей конфигурации Spring Security.
В следующем примере обработчик вернет 404 Not Found при отказе в доступе.

@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                // ...
                .exceptionHandling(exceptionHandling -> exceptionHandling
                        .accessDeniedHandler(accessDeniedHandler())
                );
    }

    @Bean
    public AccessDeniedHandler accessDeniedHandler() {
        return new CustomAccessDeniedHandler();
    }
}
public class CustomAccessDeniedHandler implements AccessDeniedHandler {
    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException {
        response.sendError(HttpStatus.NOT_FOUND.value(), HttpStatus.NOT_FOUND.getReasonPhrase());
    }
}

Есть идеи?

10000