Пример входа и выхода Spring 4 Security MVC

Сегодня мы узнаем о Примере входа в систему Spring Security. Прежде чем читать этот пост, пожалуйста, ознакомьтесь с моим предыдущим постом по ссылке “Введение в Spring 4 Security”, чтобы получить некоторые основы.

Пример входа и выхода Spring Security

В этом посте мы собираемся разработать веб-приложение Spring 4 MVC Security для предоставления возможности входа и выхода, используя опцию In-Memory. В этом примере используются конфигурация Java Spring с использованием аннотаций Spring, что означает отсутствие использования web.xml и конфигурации Spring XML (старый стиль). Если вы не знакомы с модулем безопасности Spring 3.x, ознакомьтесь с следующими постами, чтобы познакомиться с рецептом Spring Security.

  1. Пример безопасности Spring MVC с использованием в памяти, UserDetailsService и аутентификации JDBC
  2. Безопасность Spring в веб-приложении с использованием DAO, JDBC, аутентификации в памяти

Модуль безопасности Spring 4 поддерживает следующие варианты для хранения и управления учетными данными пользователя:

  1. Хранение в памяти
  2. Реляционные базы данных (СУБД)
  3. NoSQL хранилища данных
  4. LDAP

Мы будем использовать опцию “В памяти” в этом примере. Обсудим другие варианты в моих будущих сообщениях. Мы собираемся использовать Spring 4.0.2.RELEASE, Spring STS 3.7 Suite IDE, Spring TC Server 3.1 с Java 1.8 и средство сборки Maven для разработки этого примера.

Пример входа в систему Spring Security

Мы собираемся разработать логику входа и выхода из системы, используя функции безопасности Spring 4. Основная цель этого приложения заключается в том, чтобы разработать приложение без использования “web.xml” и без написания ни одной строки конфигурации Spring XML Beans. Это означает, что мы собираемся использовать функцию конфигурации Spring Java с аннотациями Spring. Мы разработаем это приложение со следующими функциями:

  1. Страница приветствия
  2. Страница входа
  3. Домашняя страница
  4. Функция выхода

Используйте следующие шаги для разработки и изучения этого простого примера входа в систему Spring 4 Security.

  • Создайте проект “Простой веб-проект Spring Maven” в Spring STS Suite со следующими данными
   Project Name : SpringMVCSecruityMavenApp
  • Обновите pom.xml следующим содержимым
<?xml version="1.0" encoding="UTF-8"?>
<project
   xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 
   https://maven.apache.org/xsd/maven-4.0.0.xsd"
   xmlns="https://maven.apache.org/POM/4.0.0" 
   xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance">

	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev</groupId>
	<artifactId>SpringMVCSecruityMavenApp</artifactId>
	<packaging>war</packaging>
	<version>1.0</version>

	<properties>
	    <java.version>1.8</java.version>
	    <spring.version>4.0.2.RELEASE</spring.version>
	    <spring.security.version>4.0.2.RELEASE</spring.security.version>
	    <servlet.api.version>3.1.0</servlet.api.version>
	    <jsp.api.version>2.2</jsp.api.version>
	    <jstl.version>1.2</jstl.version>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-web</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${spring.security.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring.security.version}</version>
		</dependency>
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>${servlet.api.version}</version>
		</dependency>		
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>jsp-api</artifactId>
			<version>${jsp.api.version}</version>
		</dependency>
		<dependency>
			<groupId>jstl</groupId>
			<artifactId>jstl</artifactId>
			<version>${jstl.version}</version>
		</dependency>
	</dependencies>	

	<build>
	    <finalName>SpringMVCSecruityMavenApp</finalName>
	    <plugins>
		<plugin>
		     <groupId>org.apache.maven.plugins</groupId>
		     <artifactId>maven-compiler-plugin</artifactId>
		     <version>3.1</version>
		     <configuration>
			<source>${java.version}</source>
			<target>${java.version}</target>
		     </configuration>
		</plugin>
		<plugin>
	           <groupId>org.apache.maven.plugins</groupId>
	           <artifactId>maven-war-plugin</artifactId>
	           <configuration>
	              <failOnMissingWebXml>false</failOnMissingWebXml>
	           </configuration>           
        	</plugin>
	    </plugins>
	</build>
</project>

ПРИМЕЧАНИЕ:- Если вы не знакомы с флагом “<failOnMissingWebXml>”, пожалуйста, прочтите в конце этого сообщения, чтобы получить хорошее представление об использовании этого элемента. – Во-первых, разработайте контроллер входа, используя аннотацию @Controller.
Controller.java

package com.journaldev.spring.web.controller;

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;

@Controller
public class LoginController {

	@RequestMapping(value = { "/"}, method = RequestMethod.GET)
	public ModelAndView welcomePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("welcomePage");
		return model;
	}

	@RequestMapping(value = { "/homePage"}, method = RequestMethod.GET)
	public ModelAndView homePage() {
		ModelAndView model = new ModelAndView();
		model.setViewName("homePage");
		return model;
	}
	
	@RequestMapping(value = "/loginPage", method = RequestMethod.GET)
	public ModelAndView loginPage(@RequestParam(value = "error",required = false) String error,
	@RequestParam(value = "logout",	required = false) String logout) {
		
		ModelAndView model = new ModelAndView();
		if (error != null) {
			model.addObject("error", "Invalid Credentials provided.");
		}

		if (logout != null) {
			model.addObject("message", "Logged out from JournalDEV successfully.");
		}

		model.setViewName("loginPage");
		return model;
	}

}

Объяснение кода:- Мы определили три метода в “LoginController” для обработки трех различных типов запросов клиентов

  1. welcomePage() будет обрабатывать все запросы клиентов, которые используют URI “/”
  2. homePage() будет обрабатывать все запросы клиентов, которые используют URI “/homePage”
  3. loginPage() будет обрабатывать все запросы клиентов, которые используют URI “/loginPage”
  4. В loginPage() мы заботимся о обработке сообщений об ошибке и выходе из системы.
  • Затем разработайте класс “LoginSecurityConfig” для предоставления функций безопасности входа и выхода с помощью Spring 4 Security API.
    LoginSecurityConfig.java
package com.journaldev.spring.secuity.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
@EnableWebSecurity
public class LoginSecurityConfig extends WebSecurityConfigurerAdapter {

	@Autowired
	public void configureGlobal(AuthenticationManagerBuilder authenticationMgr) throws Exception {
		authenticationMgr.inMemoryAuthentication()
			.withUser("journaldev")
			.password("jd@123")
			.authorities("ROLE_USER");
	}
	
	@Override
	protected void configure(HttpSecurity http) throws Exception {
		http.authorizeRequests()
			.antMatchers("/homePage").access("hasRole('ROLE_USER')")
			.and()
				.formLogin().loginPage("/loginPage")
				.defaultSuccessUrl("/homePage")
				.failureUrl("/loginPage?error")
				.usernameParameter("username").passwordParameter("password")				
			.and()
				.logout().logoutSuccessUrl("/loginPage?logout"); 
		
	}
}

Объяснение кода:- Мы определили два метода в “LoginSecurityConfig” для хранения и управления учетными данными пользователей и обеспечения функций безопасности входа и выхода.

  1. @EnableWebSecurity Аннотация используется для включения веб-безопасности в любом веб-приложении.
  2. @EnableWebMVCSecurity Аннотация используется для включения веб-безопасности в веб-приложении на основе Spring MVC.
    ПРИМЕЧАНИЕ:- @EnableWebSecurity = @EnableWebMVCSecurity + Дополнительные функции. Вот почему аннотация @EnableWebMVCSecurity устарела в Spring 4.x Framework.4. Класс “LoginSecurityConfig” или любой другой класс, который предназначен для настройки Spring Security, должен расширять класс “WebSecurityConfigurerAdapter” или реализовывать соответствующий интерфейс.
  3. Метод configureGlobal() используется для хранения и управления учетными данными пользователя.
  4. В методе configureGlobal() мы можем использовать метод authorities() для определения ролей нашего приложения, например, “ROLE_USER”. Мы также можем использовать метод roles() для этой же цели.
  5. Разница между методами authorities() и roles():
  6. Метод authorities() требует полного имени роли, например, “ROLE_USER”, а метод roles() требует имени роли, например, “USER”. Он автоматически добавит значение “ROLE_” к имени роли “USER”. ПРИМЕЧАНИЕ:- Мы разработаем другой пример, чтобы продемонстрировать роли, такие как “USER”, “ADMIN”, в моих будущих сообщениях.
  7. Важный метод для обеспечения безопасности входа и выхода – configure(HttpSecurity http)
  8. Следующий фрагмент кода используется для предотвращения несанкционированного доступа к “/homePage”. Если вы попытаетесь получить доступ к этой странице напрямую, мы будем автоматически перенаправлены на страницу “/loginPage”.
.antMatchers("/homePage").access("hasRole('ROLE_USER')")

Если мы удалим вызов метода access(“hasRole(‘ROLE_USER’)”), то мы сможем получить доступ к этой странице без входа в наше приложение. Мы настроили функции входа и выхода, используя методы formLogin() и logout().

  • Включить конфигурацию Spring MVC
    LoginApplicationConfig.java
package com.journaldev.spring.secuity.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import org.springframework.web.servlet.view.JstlView;

@EnableWebMvc
@Configuration
@ComponentScan({ "com.journaldev.spring.*" })
@Import(value = { LoginSecurityConfig.class })
public class LoginApplicationConfig {
	@Bean
	public InternalResourceViewResolver viewResolver() {
		InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
		viewResolver.setViewClass(JstlView.class);
		viewResolver.setPrefix("/WEB-INF/views/");
		viewResolver.setSuffix(".jsp");
		return viewResolver;
	}
	
}

Пояснение кода:- Мы используем класс “LoginApplicationConfig” для определения просмотров разрешений Spring MVC, чтобы избежать написания файла “web.xml”.

  1. @EnableWebMvc Аннотация используется для включения функций приложения Spring Web MVC в Spring Framework
  2. @Import Аннотация используется для импорта класса конфигурации Spring Security в этот класс.
  3. @ComponentScan Аннотация используется для сканирования компонентов в указанном пакете. Она эквивалентна “context:component-scan” в конфигурации Spring XML.
  • Инициализация Spring Security
package com.journaldev.spring.secuity.config.core;

import org.springframework.security.web.context.AbstractSecurityWebApplicationInitializer;

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

“SpringSecurityInitializer” используется для регистрации DelegatingFilterProxy для использования цепочки springSecurityFilterChain. Это позволяет избежать настройки фильтров в файле web.xml. – Инициализация приложения Spring MVC “SpringMVCWebAppInitializer” класс используется для инициализации “DispatcherServlet” без файла web.xml в конфигурации на основе аннотаций. SpringMVCWebAppInitializer.java

package com.journaldev.spring.secuity.config.core;

import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
import com.journaldev.spring.secuity.config.LoginApplicationConfig;

public class SpringMVCWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		return new Class[] { LoginApplicationConfig.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		return null;
	}

	@Override
	protected String[] getServletMappings() {
		return new String[] { "/" };
	}
	
}

ПРИМЕЧАНИЕ:-

  1. Когда мы получаем доступ к нашему приложению, по умолчанию метод getServletMappings() класса SpringMVCWebAppInitializer позволит получить доступ к корневому URL: “/”. Мы можем переопределить его для перенаправления на другой URL.
  2. Команда Spring или Pivotal работает над этой проблемой, чтобы избежать большого количества кода на Java, предложив аннотацию. Пожалуйста, проверьте это на https://jira.spring.io/browse/SPR-10359.
  • Разработать файл welcomePage.jsp
<h3>Welcome to JournalDEV Tutorials</h3>
<a href="${pageContext.request.contextPath}/loginPage">Login to Journal</a>
  • Разработать файл loginPage.jsp
<%@ taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<html>
<body onload='document.loginForm.username.focus();'>
	<h3>JournalDEV Tutorials</h3>

	<c:if test="${not empty error}"><div>${error}</div></c:if>
	<c:if test="${not empty message}"><div>${message}</div></c:if>

	<form name='login' action="<c:url value='/loginPage' />" method='POST'>
		<table>
			<tr>
				<td>UserName:</td>
				<td><input type='text' name='username' value=''></td>
			</tr>
			<tr>
				<td>Password:</td>
				<td><input type='password' name='password' /></td>
			</tr>
			<tr>
				<td colspan='2'><input name="submit" type="submit" value="submit" /></td>
			</tr>
		</table>
		<input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
	</form>
</body>
</html>
  • Разработать файл homepage.jsp
<%@taglib prefix="c" uri="https://java.sun.com/jsp/jstl/core"%>
<h3>Welcome to JournalDEV Tutorials</h3>
<ul>
	<li>Java 8 tutorial</li>
	<li>Spring tutorial</li>
	<li>Gradle tutorial</li>
	<li>BigData tutorial</li>
</ul>

<c:url value="/logout" var="logoutUrl" />
<form id="logout" action="${logoutUrl}" method="post" >
  <input type="hidden" name="${_csrf.parameterName}" value="${_csrf.token}" />
</form>
<c:if test="${pageContext.request.userPrincipal.name != null}">
	<a href="javascript:document.getElementById('logout').submit()">Logout</a>
</c:if>
  • Структура конечного проекта выглядит так:

Запустить пример Spring Security MVC Login Logout

Для запуска этого веб-приложения Spring нам нужен любой веб-контейнер, который поддерживает среды Spring 4 и Java 8 с Servlet 3.1.0.

  • Развернуть и запустить на Spring TC Server в Spring STS Suite
  • Он автоматически получает доступ к URL нашей страницы приветствия приложения, как показано ниже.
    – щелкните по ссылке “Вход в JournalDEV”, чтобы перейти на страницу входа.
    – Теперь введите неправильные данные для входа и нажмите кнопку “Вход”.
    Здесь мы можем видеть сообщение об ошибке: “Предоставлены недопустимые учетные данные”.- Теперь введите правильные данные для входа, настроенные в классе “LoginSecurityConfig”.
    После успешного входа в наше приложение мы видим домашнюю страницу приложения с ссылкой “Выход”.- нажмите на ссылку “Выход”, чтобы выйти из приложения.
    Здесь мы можем видеть, что мы успешно вышли из нашего приложения и снова перешли на страницу входа. Мы можем видеть некоторое сообщение об успешном выходе на этой странице входа.

Примечание:- Если мы рассмотрим этот пример, мы не используем файл web.xml правильно. Поскольку это веб-приложение, Maven ищет файл web.xml и выдает ошибки, если не находит его в приложении. Чтобы избежать проблем с Maven, нам нужно настроить флаг “failOnMissingWebXml” в файле pom.xml. Вот и все, что касается простого примера Spring 4 Security Module. Мы разработаем еще несколько реальных полезных примеров в моих будущих сообщениях, таких как управление ролями, функция Remember-Me, безопасность WebSocket и другие. Пожалуйста, оставьте комментарий, если вам понравилась моя публикация или у вас есть какие-либо проблемы/предложения.

Source:
https://www.digitalocean.com/community/tutorials/spring-4-security-mvc-login-logout-example