Может ли 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
и т. д. на @Controller
s.
Что не сработало для меня @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 ...")