В веб-приложении используется Spring WebMVC и Spring Security. Возник вопрос, почему не работает аннотация @PreAuthorize, если повесить её на вложенный метод в классе контроллера:
@GetMapping(value= "/method")
public String exampleForMethodPreAuthorize() {
if(methodController()){
return "forMethodPreAuthorize";
}
else return null;
}
@PreAuthorize("hasRole('ADMIN')")
public final boolean methodController(){
return true;
}
Код выше не работает, т.е. метод срабатывает для любых пользователей, даже тех, кто не имеет полномочий доступа Admin. Хотя, если эту аннотацию расположить на самом методе контроллера:
@PreAuthorize("hasRole('ADMIN')")
@GetMapping(value= "/method")
public String exampleForMethodPreAuthorize() {
return "forMethodPreAuthorize";
}
то в этом случае всё работает отлично. Естественно, в файле конфигурации сервлет-контекста используется component-scan и
<security:global-method-security pre-post-annotations="enabled"/>
Заранее спасибо за ответ.
Пожалуй, разверну оба варианта своего ответа, так как, может быть не в этом конкретном случае, но надо учитывать все возможные причины такого поведения.
Во-первых, Spring заворачивает бин контроллера в прокси-объект, перехватывающий все вызовы к методам и проверяющий соответствие прав доступа выражению в аннотации @PreAuthorize. Но прокси-объект не может перехватывать вызовы внутри целевого объекта, только вызовы извне. С этим можно столкнулся при использовании многих аннотаций, не только аннотаций Spring Security.
Во-вторых, в приложениях использующих Spring Security, до того как попасть в dispatcher servlet и после к контроллеру, запрос проходит через цепочку сервлетных фильтров.
В частности, через FilterSecurityInterceptor, который как раз проверяет истинность security expressions и отбрасывает запрос ещё до того, как контроллер получит управление, если выражение ложно.
Поэтому я удивлён, честно говоря, что Spring Security делает ещё одну проверку при вызове метода.
Sergey, ваш первый ответ натолкнул меня на мысль, благодаря чему я нашёл ответ на свой вопрос. Действительно, бин контроллера проксируется и видимо из-за этого внутренние вызовы методов не проверяются на аннотацию @PreAuthorize. Потребовалось в отдельном классе, аннотированном @Component, создать метод, который предназначен вызываться из метода контроллера и инжектировать бин данного класса в класс контроллера при помощи @Autowired. Дело в том, что механизм использования аннотации @PreAuthorize сам основан на создании прокси для бина соответствующего класса и проверкой этим прокси вызовов методов на наличие данной аннотации. Поэтому метод с аннотацией @PreAuthorize должен быть в классе-бине. Примерно так:
@Controller
public class MvcController {
@Autowired
SomeClass someClass;
@GetMapping(value= "/externalMethod")
public String exampleForExternalMethodPreAuthorize() {
if(someClass.externalMethod()){
return "forMethodPreAuthorize";
}
else return null;
}
}
@Component
public class SomeClass {
@PreAuthorize("hasRole('ADMIN')")
public boolean externalMethod() {
return true;
}
}
Как развивать веб-проекты в 2026 году: технологии, контент E-E-A-T и факторы доверия
Современные инструменты для криптотрейдинга: как технологии помогают принимать решения
Апостиль в Лос-Анджелесе без лишних нервов и бумажной волокиты
Основные этапы разработки сайта для стоматологической клиники