Spring Security 설정하기

oyeon·2021년 2월 8일
3
  • Directory 구조

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>org.edwith.webbe</groupId>
	<artifactId>securityexam</artifactId>
	<packaging>war</packaging>
	<version>0.0.1-SNAPSHOT</version>
	<name>securityexam Maven Webapp</name>
	<url>http://maven.apache.org</url>

	<properties>
		<!-- eclipse에서 웹 어플리케이션 프로젝트 작성시 web.xml파일을 작성하지 않고 java-config로 설정할 경우 
			아래의 설정이 있어야 한다. -->
		<failOnMissingWebXml>false</failOnMissingWebXml>
		<!-- spring 5.2.3이 나오는 시점에 spring security는 5.2.2가 최신버전이라서 5.2.2.RELEASE로 
			설정함 -->
		<spring.version>5.2.2.RELEASE</spring.version>
	</properties>

	<dependencies>
		<!-- servlet-api이다. tomcat에 배포될 경우엔 사용되지 않도록 하기 위해서 scope를 provided로 설정하였다. -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>javax.servlet-api</artifactId>
			<version>3.1.0</version>
			<scope>provided</scope>
		</dependency>

		<!-- jsp-api이다. tomcat에 배포될 경우엔 사용되지 않도록 하기 위해서 scope를 provided로 설정하였다. -->
		<dependency>
			<groupId>javax.servlet.jsp</groupId>
			<artifactId>javax.servlet.jsp-api</artifactId>
			<version>2.3.2-b02</version>
			<scope>provided</scope>
		</dependency>

		<!-- jstl은 tomcat이 기본 지원하지 않는다. 그렇기 때문에 tomcat에도 배포가 되야 한다. -->
		<dependency>
			<groupId>javax.servlet</groupId>
			<artifactId>jstl</artifactId>
			<version>1.2</version>
		</dependency>

		<!-- spring webmvc에 대한 의존성을 추가한다. spring webmvc에 대한 의존성을 추가하게 되면 spring-web, 
			spring-core등이 자동으로 의존성이 추가된다. -->
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-webmvc</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- java 9 이상에서 추가해줘야 한다. @PostConstruct 등을 사용하려면 필요함 -->
		<dependency>
			<groupId>javax.annotation</groupId>
			<artifactId>javax.annotation-api</artifactId>
			<version>1.3.2</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.12</version>
			<scope>test</scope>
		</dependency>

		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-test</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring Security Core -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-core</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring Security Config -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-config</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring Security Web -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-web</artifactId>
			<version>${spring.version}</version>
		</dependency>

		<!-- Spring Security JSP Custom Tags -->
		<dependency>
			<groupId>org.springframework.security</groupId>
			<artifactId>spring-security-taglibs</artifactId>
			<version>${spring.version}</version>
		</dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<artifactId>maven-compiler-plugin</artifactId>
				<version>3.7.0</version>
				<configuration>
					<source>1.8</source>
					<target>1.8</target>
					<encoding>utf-8</encoding>
				</configuration>
			</plugin>
		</plugins>
	</build>
</project>

WebAppInitializer.java

package org.edwith.webbe.securityexam.config;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

import javax.servlet.*;

/* web.xml파일을 대신하는 자바 Config 설정파일이다. Spring MVC를 사용하는 경우에
 * AbstractAnnotationConfigDispatcherServletInitializer를 상속받아서 구현하면 편리
 */
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    // Spring Config 파일을 설정한다.
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class<?>[]{ApplicationConfig.class, SecurityConfig.class};
    }

    // Spring WEB Config 파일을 설정한다.
    // WebConfig는 Bean을 RootConfig에서 설정한 곳에서부터 찾는다.
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class<?>[]{MvcConfig.class};
    }

    /* getServletMapping()은 DispatcherServlet이 매핑되기 위한 하나 혹은 여러 개의 패스를 지정한다.
     * 위의 코드에서는 애플리케이션 기본 서블릿인 /에만 매핑이 되어 있다. 
     * 그리고 이것은 애플리케이션으로 들어오는 모든 요청을 처리한다. 
     * 원래 서블릿에서는 / 을 처리하는 DefaultServlet이 설정되어 있다. 
     */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }

    // DispatcherServlet 앞에 동작하는 필터를 설정
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");

        return new Filter[]{encodingFilter};
    }
}

ApplicationConfig.java

package org.edwith.webbe.securityexam.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

// ApplicationConfig.java는 스프링 설정파일이다.
/* 레이어드 아키텍처에서 Controller가 사용하는 Bean들에 대해 설정을 한다.
 * dao, service를 컴포넌트 스캔하여 찾도록 한다.
 * 해당 패키지에 @Repository나 @Service가 붙어 있는 클래스가 있다면
 * 자동으로 빈(Bean)으로 생성하게 된다.
 * 어노테이션으로 트랜잭션을 관리하기 위해 @EnableTransactionManagement를 설정하였다.
 */
@Configuration
@ComponentScan(basePackages = {"org.edwith.webbe.securityexam.dao", "org.edwith.webbe.securityexam.service"})
public class ApplicationConfig{

}

MvcConfig.java

package org.edwith.webbe.securityexam.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.*;

// MvcConfig.java는 Spring MVC 설정파일이다.
/* Spring MVC 설정파일에는 @EnableWebMvc가 붙어있어야 한다.
 * @EnableWebMvc는 어노테이션 기반의 Spring MVC를 구성할 때 필요한
 *  Bean들을 자동으로 구성해주는 역할을 수행한다.
 *  xml로 설정할 때는  <mvc:annontation-driven/>이라는 설정 하게 되는데
 *  이와 같은 설정이라고 보면 된다.
 */
@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"org.edwith.webbe.securityexam.controller"})
public class MvcConfig implements WebMvcConfigurer {
	private final int MAX_SIZE = 10 * 1024 * 1024;
	
	// default servlet 핸들러를 설정한다.
	// 원래 서블릿은 / (모든 요청)을 처리하는 default servlet을 제공한다. 
	// 스프링에서 설정한 path는 스프링이 처리하고, 스프링이 처리하지 못한 경로에 대한 처리는
	// default servlet에게 전달하여 처리하게 된다.
	@Override
	public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
		configurer.enable();
	}

	// Spring MVC에서 jsp view 가 위치하는 경로를 설정한다.
	@Override
	public void configureViewResolvers(ViewResolverRegistry registry) {
		registry.jsp("/WEB-INF/view/", ".jsp");
	}

    //    '/' 로 요청이 오면 '/main'으로 리다이렉트 하도록 합니다.
	@Override
	public void addViewControllers(ViewControllerRegistry registry) {
		registry.addRedirectViewController("/", "/main");
	}

    //  /resources 경로에 있는 자료들을 /resources/**로 접근하게 합니다.
	@Override
	public void addResourceHandlers(final ResourceHandlerRegistry registry) {
		registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
	}
}

SecurityWebApplicationInitializer.java

package org.edwith.webbe.securityexam.config;

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

/* Spring Security를 사용하려면 AbstractSecurityWebApplicationInitializer를 
 * 상속받는 클래스를 반드시 작성해야 한다. 이 클래스가 있을 경우 
 * Spring Security가 제공하는 필터들을 사용할 수 있도록 활성화 해준다.
 */

// AbstractSecurityWebApplicationInitializer를 상속받는 클래스를 작성해야 스프링 시큐리티 필터들이 활성화된다.
public class SecurityWebApplicationInitializer extends AbstractSecurityWebApplicationInitializer {
}

SecurityConfig.java

package org.edwith.webbe.securityexam.config;

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.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;

/* Spring Security를 이용해 로그인/로그아웃/인증/인가 등을 처리하기 위한 설정 파일이다.
 * @EnableWebSecurity가 붙어 있을 경우 Spring Security를 구성하는
 * 기본적인 Bean들을 자동으로 구성해준다. WebSecurityConfigurerAdapter를 상속받으면, 
 * 특정 메소드를 오버라이딩 함으로써 좀 더 손쉽게 설정할 수 있다.
 */
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	/* configure(WebSecurity web) 메소드를 오버라이딩 하는 이유는
	 * 인증/인가가 필요 없는 경로를 설정할 필요가 있기 때문이다.
	 */
	/* Maven이전에 널리 사용된 프로젝트를 빌드할 때 사용하는 ant라는 도구가 있다. 
	 * 이 ant에서 사용하는 표기법 중에 "/경로/**" 와 같은 형식이 있는데
	 * "**"는 특정 경로 이하의 모든 것을 의미한다. 따라서 지정한 경로를 무시하라는 의미이다.
	 */
    //   /webjars/** 경로에 대한 요청은 인증/인가 처리하지 않도록 무시한다.
    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers(
                "/webjars/**");
    }

    /* configure(HttpSecurity http) 메소드를 오버라이딩 한다는 것은
     * 인증/인가에 대한 설정을 한다는 의미이다. 가장 중요한 메소드로 볼 수 있다.
     */
    /* http.csrf().disable()는 csrf() 기능을 끄라는 설정이다.
     * csrf는 보안 설정 중 post방식으로 값을 전송할 때 token을 사용해야하는 보안 설정이다.
     * csrf은 기본으로 설정되어 있는데 사용시 보안성은 높아지지만 
     * 개발초기에는 불편함이 있다는 단점이 있어서 기능을 끈 것이다.
     */
    /* disable()메소드는  HttpSecurity를 리턴한다.
     * 즉 다음과 같은 뜻이다.
     * http.authorizeRequests()
                .antMatchers("/", "/main").permitAll()
                .anyRequest().authenticated();
     */
    /*  "/"와 "/main" 경로는 누구나 접근(permitAll)할 수 있도록 한 것이며
		그외의 경로는 인증을 한 후에만 접근할 수 있다는 것을 의미한다.
     */
    //   /, /main에 대한 요청은 누구나 할 수 있지만, 그 외의 요청은 모두 인증 후 접근 가능하다.
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
                .csrf().disable()
                .authorizeRequests()
                .antMatchers("/", "/main").permitAll()
                .anyRequest().authenticated();
    }

    // 패스워드 인코더를 Bean으로 등록한다. 암호를 인코딩하거나, 
    // 인코딩된 암호와 사용자가 입력한 암호가 같은 지 확인할 때 사용한다.
    @Bean
    public PasswordEncoder encoder() {
        return new BCryptPasswordEncoder();
    }
}

MainController.java

package org.edwith.webbe.securityexam.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class MainController {
	// SecurityConfig 파일에서 /main은 누구나 접근할 수 있도록 했다.
	//  @ResponseBody 어노테이션이 붙어있을 경우엔 리턴하는 문자열을 화면에 직접 출력
    @RequestMapping("/main")
    @ResponseBody
    public String main(){
        return "main page";
    }

    @RequestMapping("/securepage")
    @ResponseBody
    public String securitypage(){
        return "secure page";
    }
}

결과

profile
Enjoy to study

0개의 댓글