Может ли Spring Security использовать @PreAuthorize в методах контроллеров Spring?

Может ли Spring Security использовать @PreAuthorize на Spring методы контроллеров?

7 ответов

Решение

Да, работает нормально.

Тебе нужно <security:global-method-security pre-post-annotations="enabled" /> в ...-servlet.xml, Также требуются прокси-серверы CGLIB, поэтому либо у ваших контроллеров не должно быть интерфейсов, либо вы должны использовать proxy-target-class = true,

См. Spring Security FAQ (выделено мной).

В веб-приложении Spring контекст приложения, который содержит bean-компоненты Spring MVC для сервлета диспетчера, часто отделен от основного контекста приложения. Он часто определяется в файле myapp-servlet.xml, где "myapp" - это имя, присвоенное Spring DispatcherServlet в web.xml. Приложение может иметь несколько DispatcherServlets, каждый со своим собственным изолированным контекстом приложения. Бины в этих "дочерних" контекстах не видны остальной части приложения. "Родительский" прикладной контекст загружается ContextLoaderListener, который вы определили в своем файле web.xml, и видим для всех дочерних контекстов. Этот родительский контекст обычно используется для определения конфигурации безопасности, включая элемент). В результате любые ограничения безопасности, применяемые к методам в этих веб-компонентах, не будут применены, поскольку компоненты не могут быть видны из контекста DispatcherServlet. Вам нужно либо переместить объявление в веб-контекст, либо переместить bean-компоненты, которые вы хотите защитить, в основной контекст приложения.

Обычно мы рекомендуем применять метод защиты на уровне сервиса, а не на отдельных веб-контроллерах.

Если вы применяете pointcut для сервисного слоя, вам нужно только установить <global-method-security> в контексте безопасности вашего приложения.

Если вы используете Spring 3.1, вы можете сделать несколько довольно крутых вещей с этим. Посмотрите на https://github.com/mohchi/spring-security-request-mapping. Это пример проекта, который интегрирует @PreAuthorize с RequestMappingHandlerMapping Spring MVC, чтобы вы могли сделать что-то вроде:

@RequestMapping("/")
@PreAuthorize("isAuthenticated()")
public String authenticatedHomePage() {
    return "authenticatedHomePage";
}

@RequestMapping("/")
public String homePage() {
    return "homePage";
}

Запрос на "/" вызовет authenticatedHomePage(), если пользователь аутентифицирован. В противном случае он вызовет homePage().

Прошло более двух лет с тех пор, как был задан этот вопрос, но из-за проблем, с которыми я столкнулся сегодня, я бы предпочел не использовать @Secured, @PreAuthorizeи т. д. на @Controllers.

Что не сработало для меня @Validated в сочетании с @Secured контроллер:

@Controller
@Secured("ROLE_ADMIN")
public class AdministrationController {

// @InitBinder here...

@RequestMapping(value = "/administration/add-product", method = RequestMethod.POST)
public String addProductPost(@ModelAttribute("product") @Validated ProductDto product, BindingResult bindingResult) {
    // ...
}

Validator просто не запускается (Spring MVC 4.1.2, Spring Security 3.2.5), и никакие проверки не выполняются.

Подобные проблемы вызваны прокси-серверами CGLIB, используемыми Spring (когда интерфейс не реализуется классом, Spring создает прокси-сервер CGLIB; если класс реализует какой-либо интерфейс, генерируется прокси-сервер JDK - документация, хорошо объясненная здесь и здесь).

Как упоминалось в ответах, которые я привел выше, не лучше использовать аннотации Spring Security на сервисном уровне, который обычно реализует интерфейсы (поэтому используются прокси-серверы JDK), поскольку это не приводит к таким проблемам.

Если вы хотите защитить веб-контроллеры, лучше использовать <http> а также <intercept-url /> которые привязаны к конкретным URL-адресам, а не к методам в контроллерах, и работают довольно хорошо. В моем случае:

<http use-expressions="true" disable-url-rewriting="true">

    ...

    <intercept-url pattern="/administration/**" access="hasRole('ROLE_ADMIN')" />

</http>

Уже есть ответ относительно того, как заставить это работать, изменяя конфигурацию xml; однако, если вы работаете с конфигурацией на основе кода, вы можете добиться того же, разместив следующую аннотацию над @Configuration учебный класс:

@EnableGlobalMethodSecurity(prePostEnabled=true)

Чтобы расширить ответ, предоставленный Энди, вы можете использовать:

@PreAuthorize("hasRole('foo')")

проверить конкретную роль.

Шаг 1: добавьте аннотацию @EnableGlobalMethodSecurity(prePostEnabled = true) в класс SecurityConfig. как это:

 @Configuration
 @EnableWebSecurity
 @EnableGlobalMethodSecurity(prePostEnabled = true)
 public class SecurityConfig extends WebSecurityConfigurerAdapter {
   .....
 }

Шаг 2: вы можете добавить аннотацию @PreAuthorize() в уровень контроллера, службы или репозитория. на уровне метода или класса. например:

@RestController
@PreAuthorize("isAuthenticated()")    
public class WebController {  
  
    @PreAuthorize("permitAll()")  
    @GetMapping("/")  
    public String home() {  
        return "Welcome home!";  
    }

    @GetMapping("/restricted")   
    public String restricted() {  
        return "restricted method";  
    }
}

или

@RestController    
public class AdminController {
   
   
   @PreAuthorize("hasRole('ADMIN')")
   @GetMapping("/admin")
   public String adminMethod() {
   }
}

Сначала вам нужно добавить эту аннотацию в ваш WebSecurityConfig, чтобы включить аннотации @Pre и @Post.

    @EnableGlobalMethodSecurity(prePostEnabled = true)

Вы также можете проверить роли / полномочия следующим образом

    @PreAuthorize("hasAuthority('ROLE_ADMIN')")

эквивалентно

    @PreAuthorize("hasRole('ROLE_ADMIN')")

Вы также можете проверить несколько ролей / полномочий следующим образом

    @PreAuthorize("hasAuthority('ROLE_ADMIN') or hasAuthority('ROLE_USER') or ...")
Другие вопросы по тегам