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)

<dependencies> 		<dependency> 			<groupId>javax.servlet</groupId> 			<artifactId>javax.servlet-api</artifactId> 			<version>3.1.0</version> 			<scope>provided</scope> 		</dependency> 		<dependency> 			<groupId>javax.servlet.jsp</groupId> 			<artifactId>jsp-api</artifactId> 			<version>2.2</version> 			<scope>provided</scope> 		</dependency> 		<dependency> 			<groupId>org.springframework</groupId> 			<artifactId>spring-context</artifactId> 			<version>4.0.5.RELEASE</version> 			<type>jar</type> 		</dependency> 		<dependency> 			<groupId>org.springframework</groupId> 			<artifactId>spring-webmvc</artifactId> 			<version>4.0.5.RELEASE</version> 			<type>jar</type> 		</dependency>  		<!-- Add Spring Security --> 		<dependency> 			<groupId>org.springframework.security</groupId> 			<artifactId>spring-security-core</artifactId> 			<version>3.2.4.RELEASE</version> 		</dependency> 		 		<dependency> 			<groupId>org.springframework.security</groupId> 			<artifactId>spring-security-web</artifactId> 			<version>3.2.4.RELEASE</version> 		</dependency>   		<dependency> 			<groupId>org.springframework.security</groupId> 			<artifactId>spring-security-config</artifactId> 			<version>3.2.4.RELEASE</version> 		</dependency> 		<!-- Add Jstl Dependencies --> 		<dependency> 			<groupId>org.apache.taglibs</groupId> 			<artifactId>taglibs-standard-jstlel</artifactId> 			<version>1.2.1</version> 		</dependency> 	</dependencies> 

Далее мы рассмотрим настройку контроллерa Spring MVC.

Контроллер (AppController.java)

package com.elennaro.sshwa.controllers;  //Import section ommited...  @Controller public class AppController {  	@RequestMapping(value = {"/", "/helloworld**"}, method = {RequestMethod.GET}) 	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;  	} } 

В контроллере будет один незащищенный ресурс и два пути к ресурсам, доступ к которым ограничен ролями администратора (admin) и супер администратора (superadmin). Вот пути к этим ресурсам:

  • /helloword будет иметь публичный доступ, к нему не будет применяться никаких ограничений безопасности.
  • /protected область с ограниченным доступом, только пользователи обладающие ролью администратора (admin) будут иметь доступ к ней.
  • /confidential область с ограниченным доступом, только пользователи обладающие ролью супер администратора будут иметь доступ к ней.

В каждом методе мы создаем и возвращаем Модель(экземпляр класса ModelAndView). Имя компонента представления так же указано в модели. Чтобы привязать имена компонентов представления (с вашего позволения я буду называть компоненты представления простонародно вьюшками, от англ. View) к конкретным файлам отображения (в нашем случае к файлам JSP), необходимо прописать класс с настройками WebConfig.java. Описание класса представлено ниже.

Настраиваем Spring MVC (WebConfig.java)

Необходимо указать фреймворку Spring где находятся компоненты представления, и как их отображать. Так же надо привязать настройки безопасности. Все это можно сделать с помощью Java класса с аннотацией @Configuration (в будущем мы будем называть такие классы конфигурационными).

WebConfig.java

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.

Настройки Безопасности (Spring Security — AppSecurityConfig.java, SecurityInit.java)

AppSecurityConfig.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);  	} } 

Наиболее важной роль в конфигурации, играют аннотации класса:

  • @Configuration
  • @EnableWebSecurity.

Важно также то, что он расширяет класс WebSecurityConfigurerAdapter.
Аннотация @EnableWebSecurity в связке с WebSecurityConfigurerAdapter классом работает над обеспечением аутентификации. По умолчанию в Spring Security встроены и активны HTTP аутентификация и аутентификация на базе веб форм.
Кроме всего, здесь прописываем пользователей с их ролями, а затем указываем адреса ресурсов с ограниченным доступом, ограничение задано по ролям. Имена и пароли пользователей, для простоты, указаны прямо в коде. Spring Security позволяет с легкостью указать другой источник для данных о пользователях, например базу данных. Обратите внимание что роли в месте где мы присваиваем их пользователю пишутся без префикса ROLE_, в то время как в указании в методе access, в котором мы, с помощью языка выражений SPEL (Spring Expression Language), задаем выражения проверки ресурса (в нашем случае выражение проверки роли пользователя hasRole(‘ROLE_имя роли’)), мы пишем роль с префиксом ROLE_. Еще одна маленькая хитрость для аутентификации: defaultSuccessUrl("/", false), установка второго параметра (alwaysUse) в false говорит Spring Security что в случае успешной авторизации можно перенаправить пользователя на ту страничку, с которой он пришел на страницу аутентификации.

Теперь в AppSecurityConfig.java у нас находятся настройки Безопасности а в WebConfig.java настройки MVC. Необходимо удостовериться, что настройки безопасности включены в основной контекст приложения (Иными словами их увидел и втянул в себя Root Application Context). Для этого можно создать класс расширяющий(наследующий) AbstractAnnotationConfigDispatcherServletInitializer. Нам нужно настроить все так чтобы определенный URL паттерн (путь к определенному ресурсу) проходил через уровень безопасности (проходил бы проверку фильтрами Spring Security). Традиционный подход подразумевал настройку сервлет фильтра, в котором мы проверяли бы учетные данные безопасности. С появлением Setvlet 3.x больше нету необходимости объявлять фильтры в web.xml, вся настройка может быть осуществлена с помощью Java классов. Как раз для этого нам нужен AbstractAnnotationConfigDispatcherServletInitializer.

SecurityInit.java

package com.elennaro.sshwa.config;  //Import section ommited...  public class SecurityInit extends AbstractSecurityWebApplicationInitializer {   } 

Для тех кто знаком с XML конфигурацией, выше приведенный код аналогичен следующему:

<filter>  <filter-name>springSecurityFilterChain</filter-name>  <filter-class>  org.springframework.web.filter.DelegatingFilterProxy  </filter-class> </filter>   <filter-mapping>  <filter-name>springSecurityFilterChain</filter-name>  <url-pattern>/*</url-pattern>  <dispatcher>ERROR</dispatcher>  <dispatcher>REQUEST</dispatcher> </filter-mapping> 

Мы закончили настройку 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[] {WebConfig.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.
Все… Осталось, прописать вьюшки и попробовать запустить приложение.

Представления (Вьюшки)

helloworld.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>  <html> 	<head> 		<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/" /> 		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 		<title>Hello World</title> 	</head> 	<body> 		<header> 			<h1>Title : ${title}</h1> 		</header> 		<section> 			<h1>Message : ${message}</h1> 		</section> 		<div>Get <a href="protected">protected</a> resource for admin.</div> 		<div>Get <a href="confidential">confidential</a> resource for superadmin.</div> 		<footer> 			<div>Updated by Alex Art from <a href="https://in2circle.com" target="_blank">in2circle.com</a></div> 		</footer> 	</body> </html> 

protected.jsp

<%@ page isELIgnored="false" %> <%@ page language="java" contentType="text/html; charset=UTF-8" 				 pageEncoding="UTF-8"%> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> 	<head>	 		<base href="${pageContext.request.scheme}://${pageContext.request.serverName}:${pageContext.request.serverPort}${pageContext.request.contextPath}/" /> 		<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> 		<title>Protected page</title> 	</head> 	<body> 		<h1>Title : ${title}</h1> 		<h1>Message : ${message}</h1>  		<c:if test="${pageContext.request.userPrincipal.name != null}"> 			<h2>Welcome : ${pageContext.request.userPrincipal.name} 				| <c:url value="login?logout" var="logoutUrl" /> 				<a href="${logoutUrl}">Log Out</a> 			</c:if> 		</h2> 		<div>Get <a href="protected">protected</a> resource for admin.</div> 		<div>Get <a href="confidential">confidential</a> resource for superadmin.</div> 		<footer> 			<div>Updated by Alex Art from <a href="https://in2circle.com" target="_blank">in2circle.com</a></div> 		</footer> 	</body> </html> 

Тут можно толко отметить, что ссылка на logout URL: login?logout.
Spring Security, сам сгенерирует страницу login.

Финиш.

Запускаем Приложение: http://localhost:8080/sshwa/helloworld/

Как мы и хотели ресурс /helloworld незащищен
Когда же мы поменяем URL на http://localhost:8080/sshwa/protected/ Spring Security перенаправит нас на /login, с формой аутентификации по умолчанию. Если мы введем неверные логин или пароль, будут отображены сообщения об ошибках, и Spring сделает редирект на URL /login?error.
Для неавторизированных пользователей Spring сначала выкинет нас в root ("/"), а при следующей попытке зайти на ресурс с неподходящей ролью, отобразит страничку с кодом 403.

Полный код приложения доступен по адресу:
github.com/elennaro/sshwa/tree/0.2-SNAPSHOT

Надеюсь в будущем у меня будет время чтобы постепенно развивать этот туториал (в планах перевести все на Spring Boot, а когда выйдет Spring Security 4.0 Release попытаться прикрутить тесты).

На основе материала из источника: javahash.com/spring-security-hello-world-example/. Код переработан, текст дополнен кажущимися мне полезными комментариями.

Это мой первый материал, постараюсь учесть все пожелания и замечания.

ссылка на оригинал статьи http://habrahabr.ru/post/226791/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *