- Spring Security Hello World Java Config
- Используемые Технологии
- Структура Проекта.
- Зависимости Maven (из pom.xml)
- Контроллер ( AppController.java )
- Настраиваем Spring MVC (WebConfig.java)
- Настройки Безопасности (Spring Security — AppSecurityConfig.java, SecurityInit.java)
- Настраиваем Диспетчер Сервлета (WebAppInitializer.java)
- Представления (Вьюшки)
- Финиш.
Spring Security Hello World Java Config
В этом материале мы постараемся написать приложение защищенное Spring Security с применением Java Config (подхода на основе использования аннотаций и классов для настройки контекста Spring приложения) вместо XML.
Используемые Технологии
- Spring 4.0.5 Release
- Spring Boot 1.0.1
- Spring Security 3.2.3 Release
- Maven
- Tomcat 8
- Servlet Api 3.1
- Java 1.8
Это будет очень простое веб приложение, Hello World на базе Spring MVC и Spring Security. Все настройки мы осуществим используя только Java классы, без единой строчки конфигурации в XML.
Начнем с рассмотрения структуры проекта.
Структура Проекта.
Зависимости Maven (из pom.xml)
javax.servlet javax.servlet-api 3.1.0 provided javax.servlet.jsp jsp-api 2.2 provided org.springframework spring-context 4.0.5.RELEASE jar org.springframework spring-webmvc 4.0.5.RELEASE jar org.springframework.security spring-security-core 3.2.4.RELEASE org.springframework.security spring-security-web 3.2.4.RELEASE org.springframework.security spring-security-config 3.2.4.RELEASE org.apache.taglibs taglibs-standard-jstlel 1.2.1
Далее мы рассмотрим настройку контроллерa Spring MVC.
Контроллер ( AppController.java )
package com.elennaro.sshwa.controllers; //Import section ommited. @Controller public class AppController < @RequestMapping(value = , method = ) public ModelAndView welcomePage() < ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security Tutorial"); model.addObject("message", "Welcome Page !"); model.setViewName("helloworld"); return model; >@RequestMapping(value = "/protected**", method = RequestMethod.GET) public ModelAndView protectedPage() < ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security 3.2.4 Hello World Tutorial"); model.addObject("message", "This is protected page - Only for Admin Users!"); model.setViewName("protected"); return model; >@RequestMapping(value = "/confidential**", method = RequestMethod.GET) public ModelAndView adminPage() < ModelAndView model = new ModelAndView(); model.addObject("title", "Spring Security 3.2.4 Hello World Tutorial"); model.addObject("message", "This is confidential page - Need Super Admin Role!"); model.setViewName("protected"); return model; >>
- /helloword будет иметь публичный доступ, к нему не будет применяться никаких ограничений безопасности.
- /protected область с ограниченным доступом, только пользователи обладающие ролью администратора (admin) будут иметь доступ к ней.
- /confidential область с ограниченным доступом, только пользователи обладающие ролью супер администратора будут иметь доступ к ней.
Настраиваем Spring MVC (WebConfig.java)
Необходимо указать фреймворку Spring где находятся компоненты представления, и как их отображать. Так же надо привязать настройки безопасности. Все это можно сделать с помощью Java класса с аннотацией @Configuration (в будущем мы будем называть такие классы конфигурационными).
package com.elennaro.sshwa.config.application; //Import section ommited. @Configuration @EnableWebMvc @ComponentScan(< "com.elennaro.sshwa.config", "com.elennaro.sshwa.controllers" >) //@Import(< AppSecurityConfig.class >) public class WebConfig < @Bean public InternalResourceViewResolver viewResolver() < InternalResourceViewResolver viewResolver = new InternalResourceViewResolver(); viewResolver.setViewClass(JstlView.class); viewResolver.setPrefix("/WEB-INF/views/"); viewResolver.setSuffix(".jsp"); return viewResolver; >>
- Объявляем конфигурационный класс (с помощью аннотации @Configuration ).
- С помощью аннотации @ComponentScan указываем фреймворку Spring, что компоненты надо искать внутри пакетов com.elennaro.sshwa.config и com.elennaro.sshwa.controllers . Тут специалисты советуют: лучше всегда указывать конкретные пакеты, а не всё сразу с помощью * . То есть мы могли указать все пакеты: com.elennaro.sshwa.* и все бы работало. Но появись у нас в другом месте такой же @ComponentScan мы бы в лучшем случае пересканировали все пакеты дважды, а в худшем наткнулись на пару неприятных ошибок.
- Указываем что вьюшки будут лежать в директории /WEB-INF/views/
- Импортируем класс с настройками безопасности, собственно сам конфигуратор Spring Security (с помощью аннотации @Import(< AppSecurityConfig.class >)) . Эта строчка специально закомментирована в коде. Хотелось показать, что класс с настройками безопасности( AppSecurityConfig.java ) отмеченный (как вы увидите ниже) аннотацией @Configuration будет автоматически найден и подключен базовым контекстом апликации Spring фреймворка, потому что путь к пакету содерфащему класс AppSecurityConfig указан в аннотации @ComponentScan . Благодаря этому Spring найдет и подключит конфигурационный класс автоматически.
- Отметим что сам контроллер AppController.java тоже попадет под сканирование компонентов, так как путь к содержащему его пакету указан в @ComponentScan(< "com.elennaro.sshwa.config", "com.elennaro.sshwa.controllers" >) и аннотация @Controller указана для этого класса.
Настройки Безопасности (Spring Security — AppSecurityConfig.java, SecurityInit.java)
package com.elennaro.sshwa.config; //Import section ommited. @Configuration @EnableWebSecurity public class AppSecurityConfig extends WebSecurityConfigurerAdapter < @Autowired public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception < auth.inMemoryAuthentication().withUser("user").password("user").roles("USER"); auth.inMemoryAuthentication().withUser("admin").password("admin").roles("ADMIN"); auth.inMemoryAuthentication().withUser("superadmin").password("superadmin").roles("SUPERADMIN"); >@Override protected void configure(HttpSecurity http) throws Exception < http.authorizeRequests() .antMatchers("/protected/**").access("hasRole('ROLE_ADMIN')") .antMatchers("/confidential/**").access("hasRole('ROLE_SUPERADMIN')") .and().formLogin().defaultSuccessUrl("/", false); >>
Теперь в AppSecurityConfig.java у нас находятся настройки Безопасности а в WebConfig.java настройки MVC. Необходимо удостовериться, что настройки безопасности включены в основной контекст приложения (Иными словами их увидел и втянул в себя Root Application Context). Для этого можно создать класс расширяющий(наследующий) AbstractAnnotationConfigDispatcherServletInitializer. Нам нужно настроить все так чтобы определенный URL паттерн (путь к определенному ресурсу) проходил через уровень безопасности (проходил бы проверку фильтрами Spring Security). Традиционный подход подразумевал настройку сервлет фильтра, в котором мы проверяли бы учетные данные безопасности. С появлением Setvlet 3.x больше нету необходимости объявлять фильтры в web.xml, вся настройка может быть осуществлена с помощью Java классов. Как раз для этого нам нужен AbstractAnnotationConfigDispatcherServletInitializer .
package com.elennaro.sshwa.config; //Import section ommited. public class SecurityInit extends AbstractSecurityWebApplicationInitializer
springSecurityFilterChain org.springframework.web.filter.DelegatingFilterProxy springSecurityFilterChain /* ERROR REQUEST
Мы закончили настройку MVC и Spring Security. Осталось настроить Диспетчер Сервлета, который отвечает за инициализацию Spring MVC и меппинг URL паттернов. Опять же мы откажемся от традиционной настройки Диспетчера Сервлета через web.xml и будем использовать Java Классы.
Настраиваем Диспетчер Сервлета (WebAppInitializer.java)
WebAppInitializer.java
package com.elennaro.sshwa.config; //Import section ommited. public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer< @Override protected Class[] getRootConfigClasses() < return new Class[] ; // We dont need any special servlet config yet. > @Override protected Class[] getServletConfigClasses() < return null; >@Override protected String[] getServletMappings() < return new String[] ; > >
Здесь мы настроили мэпинг сервлета на “/” и поэтому все запросы будут перехвачены Диспетчером Сервлета Spring.
Отметим что, наш класс WebAppInitializer.java наследует AbstractAnnotationConfigDispatcherservletinitializergetRootConfigClasses, замещая его метод getRootConfigClasses так чтобы вернуть объявленный нами класс настроек приложения WebConfig.class .
Все… Осталось, прописать вьюшки и попробовать запустить приложение.
Представления (Вьюшки)
://$:$$/" /> Title : $ Message : $ Get protected resource for admin. Get confidential resource for superadmin.
://$:$$/" /> Title : $ Message : $ Welcome : $ " method="post"> " value="$" />
Get protected resource for admin. Get confidential resource for superadmin.
Тут можно толко отметить, что ссылка на logout URL: logout и что по умолчанию тип logout запроса должен быть POST и запрос logout должен содержать csrf token.
Spring Security, сам сгенерирует страницу login.
Финиш.
Как мы и хотели ресурс /helloworld незащищен
Когда же мы поменяем URL на http://localhost:8080/sshwa/protected/ Spring Security перенаправит нас на /login, с формой аутентификации по умолчанию. Если мы введем неверные логин или пароль, будут отображены сообщения об ошибках, и Spring сделает редирект на URL /login?error.
Для неавторизированных пользователей Spring сначала выкинет нас в root («/»), а при следующей попытке зайти на ресурс с неподходящей ролью, отобразит страничку с кодом 403.
Надеюсь в будущем у меня будет время чтобы постепенно развивать этот туториал (в планах перевести все на Spring Boot, а когда выйдет Spring Security 4.0 Release попытаться прикрутить тесты).
На основе материала из источника: javahash.com/spring-security-hello-world-example. Код переработан, текст дополнен кажущимися мне полезными комментариями.
Это мой первый материал, постараюсь учесть все пожелания и замечания.
P.S. Спасибо drno-reg, за то, что нашли ошибку при logout (текст и исходники были обновлены).