Spring 4 Security MVC 로그인 로그아웃 예제

오늘은 Spring Security 로그인 예제에 대해 배우게 될 것입니다. 이 게시물을 읽기 전에, 기본 지식을 얻기 위해 이전 게시물인 “Spring 4 Security 소개”를 참고하십시오.

Spring Security 로그인 로그아웃 예제

이 게시물에서는 In-Memory 옵션을 사용하여 로그인 및 로그아웃 기능을 제공하기 위해 Spring 4 MVC Security 웹 애플리케이션을 개발할 것입니다. 이 예제는 web.xml과 Spring XML 구성(이전 스타일)을 사용하지 않고 Spring Java Config와 Spring Annotations을 사용합니다. Spring 3.x Security 모듈에 익숙하지 않은 경우, 먼저 다음 게시물을 참고하여 Spring Security 레시피를 경험해보십시오.

  1. 인메모리, UserDetailsService 및 JDBC 인증을 사용한 Spring MVC Security 예제
  2. DAO, JDBC, In-Memory 인증을 사용한 서블릿 웹 애플리케이션의 Spring Security

Spring 4 Security 모듈은 다음과 같은 옵션을 지원하여 사용자 자격 증명을 저장하고 관리합니다:

  1. In-Memory 저장소
  2. 관계형 데이터베이스(RDBMS)
  3. No SQL 데이터 저장소
  4. LDAP

이 예제에서는 “In-Memory Store” 옵션을 사용할 것입니다. 다른 옵션에 대해서는 이후에 논의할 예정입니다. 이 예제를 개발하기 위해 Spring 4.0.2.RELEASE, Spring STS 3.7 Suite IDE, Spring TC Server 3.1 with Java 1.8 및 Maven 빌드 도구를 사용할 것입니다.

Spring Security 로그인 예제

Spring 4 보안 기능을 사용하여 로그인 및 로그아웃 로직을 개발할 예정입니다. 이 응용 프로그램의 주요 목표는 “web.xml”을 사용하지 않고 Spring XML Beans 구성에 한 줄도 작성하지 않고 응용 프로그램을 개발하는 것입니다. 이것은 Spring Java Config 기능과 Spring Annotations을 사용할 것을 의미합니다. 다음과 같은 기능을 갖춘 이 응용 프로그램을 개발할 것입니다:

  1. 환영 페이지
  2. 로그인 페이지
  3. 홈 페이지
  4. 로그아웃 기능

이 Spring 4 보안 간단한 로그인 예제를 개발하고 탐색하기 위해 다음 단계를 따라주시기 바랍니다.

  • 다음 세부 정보로 Spring STS Suite에서 “Simple Spring Web Maven” 프로젝트를 생성합니다.
   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” 플래그에 대해 알지 못하는 경우, 이 게시물 끝에서 이 요소 사용에 대한 이해를 얻기 위해 읽어보십시오.- 첫째, Spring의 @Controller 주석을 사용하여 Login Controller를 개발합니다.
LoginController.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()는 “/homePage” URI를 사용하는 모든 클라이언트 요청을 처리합니다.
  3. loginPage()는 “/loginPage” URI를 사용하는 모든 클라이언트 요청을 처리합니다.
  4. loginPage()에서는 오류 및 로그아웃 메시지를 처리하는 데 주의합니다.
  • 그런 다음 Spring 4 Security API를 사용하여 로그인 및 로그아웃 보안 기능을 제공하기 위해 “LoginSecurityConfig” 클래스를 개발합니다.
    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에서 사용되지 않습니다. “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’)”) 메소드 호출을 제거하면, 우리는 응용 프로그램에 로그인하지 않고도 이 페이지에 접근할 수 있습니다.13. 우리는 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 View Resolvers를 정의하여 “web.xml” 파일을 작성하지 않도록 합니다.

  1. @EnableWebMvc 어노테이션은 Spring 프레임워크에서 Spring 웹 MVC 애플리케이션 기능을 활성화하는 데 사용됩니다.
  2. @Import 어노테이션은 이 클래스에 Spring Security 구성 클래스를 가져오는 데 사용됩니다.
  3. @ComponentScan 어노테이션은 지정된 패키지에서 컴포넌트 스캔을 수행하는 데 사용됩니다. 이는 Spring XML 구성의 “context:component-scan“과 동일합니다.
  • Spring Security 초기화
package com.journaldev.spring.secuity.config.core;

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

public class SpringSecurityInitializer extends AbstractSecurityWebApplicationInitializer {

}

“SpringSecurityInitializer”는 DelegatingFilterProxy를 등록하여 springSecurityFilterChain을 사용합니다. 이는 Filters 구성을 web.xml 파일에 작성하지 않도록 합니다. – Spring MVC 애플리케이션 초기화
“SpringMVCWebAppInitializer” 클래스는 주석 기반 구성에서 web.xml 파일 없이 “DispatcherServlet”을 초기화하는 데 사용됩니다. 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. 우리가 애플리케이션에 접근할 때, 기본적으로 SpringMVCWebAppInitializer의 getServletMappings()은 루트 URL인 “/”에 접근할 수 있도록 허용합니다. 우리는 다른 URL로 전달하기 위해 이를 재정의할 수 있습니다.
  2. 스프링 또는 Pivotal 팀은 이 문제를 해결하기 위해 어노테이션을 도입하여 이 정도의 자바 코드를 피할 수 있도록 노력하고 있습니다. 이 내용은 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 로그인 로그아웃 예제 실행하기

이 Spring 웹 애플리케이션을 실행하기 위해서는 Spring 4와 Java 8 환경을 지원하는 Servlet 3.1.0 컨테이너를 사용하는 웹 컨테이너가 필요합니다.

  • Spring STS Suite에서 Spring TC Server에 배포하고 실행하세요.
  • 다음과 같이 자동으로 우리의 애플리케이션 환영 페이지 URL에 액세스합니다.
    – “JournalDEV에 로그인” 링크를 클릭하여 로그인 페이지에 액세스합니다.
    – 이제 잘못된 로그인 세부 정보를 제공하고 “로그인” 버튼을 클릭합니다.
    여기에서 이 오류 메시지를 관찰할 수 있습니다. “유효하지 않은 자격 증명이 제공되었습니다.” – 이제 “LoginSecurityConfig” 클래스에 구성된 올바른 로그인 세부 정보를 제공합니다.
    애플리케이션에 성공적으로 로그인한 후에는 “로그아웃” 링크와 함께 애플리케이션 홈페이지를 볼 수 있습니다. – “로그아웃” 링크를 클릭하여 애플리케이션에서 로그아웃합니다.
    여기에서 우리는 애플리케이션에서 성공적으로 로그아웃되어 다시 로그인 페이지로 리디렉션되었음을 관찰할 수 있습니다. 이 로그인 페이지에 일부 로그아웃 성공 메시지를 관찰할 수 있습니다.

참고: 이 예제를 관찰하면 우리는 web.xml 파일을 사용하지 않고 있습니다. 웹 애플리케이션인 경우 Maven은 웹.xml 파일을 찾고, 애플리케이션에서 찾지 못할 경우 일부 오류를 발생시킵니다. Maven 관련 문제를 피하기 위해 pom.xml 파일에 “” 플래그를 구성해야 합니다. 이것이 Spring 4 보안 모듈 간단한 예제에 관한 전부입니다. 저는 역할 관리, Remember-Me 기능, WebSocket 보안 등과 같은 실시간 유용한 예제를 내가 다음 글에서 개발할 것입니다. 내 글을 좋아하시거나 문제/제안이 있으면 댓글을 남겨주세요.

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