Использование @PreAuthorize на вложенном методе

354
25 января 2018, 19:16

В веб-приложении используется 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"/>

Заранее спасибо за ответ.

Answer 1

Пожалуй, разверну оба варианта своего ответа, так как, может быть не в этом конкретном случае, но надо учитывать все возможные причины такого поведения.

Во-первых, Spring заворачивает бин контроллера в прокси-объект, перехватывающий все вызовы к методам и проверяющий соответствие прав доступа выражению в аннотации @PreAuthorize. Но прокси-объект не может перехватывать вызовы внутри целевого объекта, только вызовы извне. С этим можно столкнулся при использовании многих аннотаций, не только аннотаций Spring Security.

Во-вторых, в приложениях использующих Spring Security, до того как попасть в dispatcher servlet и после к контроллеру, запрос проходит через цепочку сервлетных фильтров.

В частности, через FilterSecurityInterceptor, который как раз проверяет истинность security expressions и отбрасывает запрос ещё до того, как контроллер получит управление, если выражение ложно.

Поэтому я удивлён, честно говоря, что Spring Security делает ещё одну проверку при вызове метода.

Answer 2

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;
    }
}
READ ALSO
Как получить данные из Service в класс

Как получить данные из Service в класс

Есть класс который получает данные для фрагмента

285
Hibernate + SQLite не могут создать таблицу

Hibernate + SQLite не могут создать таблицу

В моем проекте необходима база данных SQLite, с которой я буду работать через HibernateРаботаю в Net Beans, ОС Ubuntu 17

239
Хостинг для Android приложения

Хостинг для Android приложения

Необходим бесплатный хостинг для апробации сервера на JavaТак же необходима поддержка SQLite или другой БД

205