Guide_Securing a Web Application

Dev.Hammy·2023년 12월 16일
0

Spring Guides

목록 보기
13/46

이 가이드는 Spring Security로 보호되는 리소스를 사용하여 간단한 웹 애플리케이션을 만드는 과정을 안내합니다.

What You Will Build

고정된 사용자 목록이 지원되는 로그인 양식으로 페이지를 보호하는 Spring MVC 애플리케이션을 빌드합니다.

build.gradle

plugins {
	id 'java'
	id 'org.springframework.boot' version '3.2.0'
	id 'io.spring.dependency-management' version '1.1.4'
}

group = 'guides'
version = '0.0.1-SNAPSHOT'

java {
	sourceCompatibility = '17'
}

repositories {
	mavenCentral()
}

dependencies {
	implementation 'org.springframework.boot:spring-boot-starter-security'
	implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
	implementation 'org.springframework.boot:spring-boot-starter-web'
	implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6'
	testImplementation 'org.springframework.boot:spring-boot-starter-test'
	testImplementation 'org.springframework.security:spring-security-test'
}

tasks.named('test') {
	useJUnitPlatform()
}

Create an Unsecured Web Application

웹 애플리케이션에 보안을 적용하려면 먼저 보안을 설정할 웹 애플리케이션이 필요합니다. 이 섹션에서는 간단한 웹 애플리케이션을 만드는 과정을 안내합니다. 그런 다음 다음 섹션에서 Spring Security를 사용하여 이를 보호합니다.

웹 애플리케이션에는 홈 페이지와 "Hello, World" 페이지라는 두 가지 간단한 보기가 포함되어 있습니다. 홈 페이지는 다음 Thymeleaf 템플릿(src/main/resources/templates/home.html)에 정의되어 있습니다:

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
    <head>
        <title>Spring Security Example</title>
    </head>
    <body>
        <h1>Welcome!</h1>

        <p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
    </body>
</html>

이 간단한 보기에는 다음 Thymeleaf 템플릿(src/main/resources/templates/hello.html)에 정의된 /hello 페이지에 대한 링크가 포함되어 있습니다.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1>Hello world!</h1>
    </body>
</html>

웹 애플리케이션은 Spring MVC를 기반으로 합니다. 결과적으로 Spring MVC를 구성하고 이러한 템플릿을 노출하도록 뷰 컨트롤러를 설정해야 합니다. 다음 목록(src/main/java/guides/securingweb/MvcConfig.java)은 애플리케이션에서 Spring MVC를 구성하는 클래스를 보여줍니다.

package guides.securingweb;


import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
public class MvcConfig implements WebMvcConfigurer {

    public void addViewControllers(ViewControllerRegistry registry){
        registry.addViewController("/home").setViewName("home");
        registry.addViewController("/").setViewName("home");
        registry.addViewController("/hello").setViewName("hello");
        registry.addViewController("/login").setViewName("login");
    }
    
}

addViewControllers() 메서드(WebMvcConfigurer에서 동일한 이름의 메서드를 재정의함)는 4개의 뷰 컨트롤러를 추가합니다. 뷰 컨트롤러 중 두 개는 이름이 home(home.html에 정의됨)인 뷰를 참조하고, 다른 하나는 hello(hello.html에 정의됨)라는 뷰를 참조합니다. 네 번째 뷰 컨트롤러는 login이라는 또 다른 뷰를 참조합니다. 다음 섹션에서 해당 뷰를 만듭니다.

이 시점에서 "애플리케이션 실행"으로 이동하여 로그인할 필요 없이 애플리케이션을 실행할 수 있습니다.

이제 보안되지 않은 웹 애플리케이션이 있으므로 보안을 추가할 수 있습니다.

스프링 보안 설정

권한이 없는 사용자가 /hello의 인사말 페이지를 보는 것을 방지한다고 가정해 보겠습니다. 지금처럼 방문자가 홈페이지의 링크를 클릭하면 아무런 장애 없이 인사말을 볼 수 있습니다. 방문자가 해당 페이지를 보기 전에 로그인하도록 강제하는 장벽을 추가해야 합니다.

애플리케이션에서 Spring Security를 구성하면 됩니다. Spring Security가 classpath에 있는 경우 Spring Boot는 "기본" 인증을 사용하여 모든 HTTP 엔드포인트를 자동으로 보호합니다. 그러나 보안 설정을 추가로 사용자 정의할 수 있습니다. 가장 먼저 해야 할 일은 classpath에 Spring Security를 추가하는 것입니다.

Gradle을 사용하면 다음 목록에 표시된 것처럼 build.gradledependencies 클로저에 세 줄(애플리케이션용 하나, Thymeleaf 및 Spring Security 통합용 하나, 테스트용 하나)을 추가해야 합니다.

implementation 'org.springframework.boot:spring-boot-starter-security'
//  Temporary explicit version to fix Thymeleaf bug
implementation 'org.thymeleaf.extras:thymeleaf-extras-springsecurity6:3.1.1.RELEASE'
testImplementation 'org.springframework.security:spring-security-test'

다음 보안 구성(src/main/java/guides/securingweb/WebSecurityConfig.java)은 인증된 사용자만 비밀 인사말을 볼 수 있도록 보장합니다.

package guides.securingweb;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;
import org.springframework.security.web.SecurityFilterChain;

@Configuration
@EnableWebSecurity
public class WebSecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .authorizeHttpRequests((requests) -> requests.requestMatchers("/", "/home").permitAll().anyRequest().authenticated()
                )
                .formLogin((form) -> form.loginPage("/login").permitAll()
                )
                .logout((logout) -> logout.permitAll());
                
        return http.build();
    }
    
    @Bean
    public UserDetailsService userDetailsService(){
        UserDetails user = User.withDefaultPasswordEncoder()
                .username("user")
                .password("password")
                .roles("USER")
                .build();
        
        return new InMemoryUserDetailsManager(user);
    }
}

WebSecurityConfig 클래스는 @EnableWebSecurity로 주석 처리되어 Spring Security의 웹 보안 지원을 활성화하고 Spring MVC 통합을 제공합니다. 또한 웹 보안 구성에 대한 몇 가지 세부 사항을 설정하기 위해 두 개의 Bean을 노출합니다.

SecurityFilterChain 빈은 어떤 URL 경로를 보호해야 하는지, 어떤 경로를 보호하지 말아야 하는지 정의합니다. 특히 //home 경로는 인증이 필요하지 않도록 구성됩니다. 다른 모든 경로는 인증되어야 합니다.

사용자가 성공적으로 로그인하면 인증이 필요한 이전에 요청한 페이지로 리디렉션됩니다. 사용자 정의 /login 페이지(loginPage()에 의해 지정됨)가 있으며 모든 사람이 이를 볼 수 있습니다.

UserDetailsService 빈은 단일 사용자로 메모리 내 사용자 저장소를 설정합니다. 해당 사용자에게는 user 이름, 비밀번호 password, role USER가 부여됩니다.

이제 로그인 페이지를 생성해야 합니다. login 뷰를 위한 뷰 컨트롤러가 이미 있으므로 다음 목록(src/main/resources/templates/login.html)에서 볼 수 있듯이 로그인 뷰 자체만 생성하면 됩니다.

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
    <head>
        <title>Spring Security Example </title>
    </head>
    <body>
        <div th:if="${param.error}">
            Invalid username and password.
        </div>
        <div th:if="${param.logout}">
            You have been logged out.
        </div>
        <form th:action="@{/login}" method="post">
            <div><label> User Name : <input type="text" name="username"/> </label></div>
            <div><label> Password: <input type="password" name="password"/> </label></div>
            <div><input type="submit" value="Sign In"/></div>
        </form>
    </body>
</html>

이 Thymeleaf 템플릿은 사용자 이름과 비밀번호를 캡처하여 /login에 게시하는 양식을 제공합니다. 구성된 대로 Spring Security는 해당 요청을 가로채고 사용자를 인증하는 필터를 제공합니다. 사용자가 인증에 실패하면 페이지가 /login?error로 리디렉션되고 페이지에 적절한 오류 메시지가 표시됩니다. 로그아웃에 성공하면 애플리케이션이 /login?logout으로 전송되고 페이지에 적절한 성공 메시지가 표시됩니다.

마지막으로 방문자에게 현재 사용자 이름을 표시하고 로그아웃할 수 있는 방법을 제공해야 합니다. 이렇게 하려면 hello.html을 업데이트하여 현재 사용자에게 인사하고 Sign Out 양식을 포함하십시오(src/main/resources/templates/hello.html의 다음 목록 참조).

<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org"
      xmlns:sec="https://www.thymeleaf.org/thymeleaf-extras-springsecurity6">
    <head>
        <title>Hello World!</title>
    </head>
    <body>
        <h1 th:inline="text">Hello <span th:remove="tag" sec:authentication="name">thymeleaf</span>!</h1>
        <form th:action="@{/logout}" method="post">
            <input type="submit" value="Sign Out"/>
        </form>
    </body>
</html>

Thymeleaf와 Spring Security의 통합을 사용하여 사용자 이름을 표시합니다. "로그아웃" 양식은 /logout에 POST를 제출합니다. 성공적으로 로그아웃되면 사용자를 /login?logout으로 리디렉션합니다.

Thymeleaf 3.1은 더 이상 HttpServletRequest에 대한 액세스를 제공하지 않으므로 HttpServletRequest#getRemoteUser()를 사용하여 현재 인증된 사용자에 액세스할 수 없습니다.

애플리케이션 실행

Spring 초기화는 당신을 위해 애플리케이션 클래스를 생성합니다. 이 경우 클래스를 수정할 필요가 없습니다. 다음 목록(src/main/java/guides/securingweb/SecuringWebApplication.java)은 애플리케이션 클래스를 보여줍니다.

package guides.securingweb;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class SecuringWebApplication {

	public static void main(String[] args) {
		SpringApplication.run(SecuringWebApplication.class, args);
	}

}

링크를 클릭하면 /hello의 인사말 페이지로 이동하려고 시도합니다. 그러나 해당 페이지는 보안되어 있고 아직 로그인하지 않았으므로 다음 이미지와 같이 로그인 페이지로 이동합니다.

로그인 페이지에서 사용자 이름과 비밀번호 필드에 각각 사용자와 비밀번호를 입력하여 테스트 사용자로 로그인합니다. 로그인 양식을 제출하면 인증을 받은 후 다음 이미지와 같이 인사말 페이지로 이동됩니다.

로그아웃 버튼을 클릭하면 인증이 취소되며, 로그아웃되었다는 메시지와 함께 로그인 페이지로 돌아갑니다.

0개의 댓글