Spring Security — защита методов (method security)
С помощью Spring Security можно устанавливать защиту не только на вход в приложение, но и на любой выполняемый метод. Как настроить защиту методов в приложении читайте ниже.
Используемые технологии
- Spring 4.2.2.RELEASE
- Spring Security 4.0.2.RELEASE
- MySQL 5.6.25
- JSP 1.2.1
- IntelliJ IDEA 14
- Maven 3.2.5
1. Описание задачи
Включить защиту методов в Spring Security 4.0.2. Показать использование аннотаций @Secured, @PreAuthorize для защиты методов.
2. Структура проекта
Т.к. здесь будет описаны только несколько файлов, то для понимания что делают другие файлы проекта рекомендую к прочтению Spring Security – username password remember me (сохранение пароля пользователя для автоматической аутентификации).
Относительно предыдущей части добавлен интерфейс MainInterface и его реализация.
3. Настройка dispatcher-servlet.xml
В настройках изменению подвергся только этот xml.
1 2 3 4 |
<!-- Включает поддержку аннотаций по безопасности--> <security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/> |
Обращаю внимание, что эта настройка добавлена в dispatcher-servlet.xml, а не в spring-security-config.xml.
Полный листинг dispatcher-servlet.xml:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context" xmlns:security="http://www.springframework.org/schema/security" xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security.xsd"> <!-- Dispatcher Servlet - определяет инфраструктуру обработки запросов сервлетом --> <!-- Включает поддержку @Controller и др. в Spring MVC --> <mvc:annotation-driven/> <!-- Обрабатывает HTTP GET запросы для указанного каталога более эффективно для статических ресурсов --> <mvc:resources mapping="/resources/**" location="resources"/> <!-- Определяет какое представление рендерить на основании вычеслений в @Controller (return ModelAndView) --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="prefix" value="/WEB-INF/views/"/> <property name="suffix" value=".jsp"/> </bean> <!-- Сканирует пакет для поиска бинов --> <context:component-scan base-package="ru.javastudy"/> <!-- Включает Spring аннотации вроде @Autowired--> <context:annotation-config/> <!-- Включает поддержку аннотаций по безопасности--> <security:global-method-security secured-annotations="enabled" jsr250-annotations="enabled" pre-post-annotations="enabled"/> </beans> |
4. Интерфейс MainInterface и реализация
Защиту методов рекомендуется включать на уровне интерфейса.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
package ru.javastudy.services.intf; import org.springframework.security.access.annotation.Secured; import org.springframework.security.access.prepost.PreAuthorize; import java.security.Principal; public interface MainInterface { // @Secured(value = {"ROLE_USER","ROLE_ADMIN"}) @Secured(value = {"ROLE_ADMIN"}) String showMeMessage(); @PreAuthorize("#user.name == 'admin'") void preAuthorizeMessageExample(Principal user); } |
Выполнить метод showMessage сможет пользователь с ролью ROLE_ADMIN. Метод preAuthorizeMessageExample сможет выполнить только тот пользователь, имя которого совпадет с ‘admin’. Заметьте как можно использовать Expression Language в Spring Security.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
package ru.javastudy.services.impl; import org.springframework.stereotype.Service; import ru.javastudy.services.intf.MainInterface; import java.security.Principal; @Service("showMessageService") public class MainInterfaceImpl implements MainInterface { public String showMeMessage() { return "Message from MainInterfaceImpl.showMeMessage()"; } public void preAuthorizeMessageExample(Principal user) { System.out.println("PreAuthorizeMessageExample" +user.getName()); } } |
Обычный вывод в консоль сообщения, чтобы было понятно отработал ли метод. Результат первого метода добавляется в модель Spring MVC и используется на странице secondPage.jsp.
5. Контроллер MainController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
package ru.javastudy.controllers; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import ru.javastudy.services.intf.MainInterface; import java.security.Principal; @Controller public class MainController { private static final Logger logger = LoggerFactory.getLogger(MainController.class); @Autowired private MainInterface securedMessage; @RequestMapping(value = "/second") public ModelAndView indexPage(Principal user) { System.out.println("Before secured method"); logger.info(securedMessage.showMeMessage()); securedMessage.preAuthorizeMessageExample(user); return new ModelAndView("secondPage", "methodMessage", securedMessage.showMeMessage()); } @RequestMapping(value = "/admin") public String adminPage() { return "adminPage"; } @RequestMapping(value ="/welcome", method = RequestMethod.GET) public ModelAndView login(@RequestParam(value = "error", required = false) String error) { ModelAndView model = new ModelAndView(); if (error != null) { model.addObject("error", "Invalid username or password!"); } model.setViewName("welcomePage"); return model; } } |
При попытке перейти на страницу secondPage.jsp (клик по ссылке /second, подробности в статье, указанной в начале), на которую имеется доступ для пользователей с правами ROLE_USER и ROLE_ADMIN будет предпринята попытка вызвать два метода из интерфейса.
Если у пользователя нет соответствующих прав, то произойдет переадресация на страницу accessDenied.
Попробуйте войти под пользователем user (без роли ROLE_ADMIN). У user будет доступ к странице /second, но при попытке вызвать метод доступ будет перекрыт, т.к. метод доступен только для ROLE_ADMIN.
Для пользователя admin сообщение из метода showMeMessage() успешно отображено на странице secondPage.jsp
Исходные коды
Spring Security MethodSecurity
11