본문 바로가기
Web developer/Spring

[Spring] 기본 설정 + Spring Security 설정

by doongjun 2021. 8. 20.

새로운 프로젝트를 생성할 때마다 해줘야 하는 기본설정들을 기억하기 위해 작성하는 글이다.

web.xml 설정이 아닌 자바 설정이고 Spring Security 설정 또한 포함하여 작성하였다.

+JDBC

더보기

pom.xml

<?xml version="1.0" encoding="UTF-8"?>

<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/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.edwith.webbe</groupId>
  <artifactId>securityexam</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <packaging>war</packaging>

  <name>securityexam Maven Webapp</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
    <!-- 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>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.12</version>
      <scope>test</scope>
    </dependency>
    
	<!-- 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>
    
    <!-- mysql jdbc driver -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.19</version>
    </dependency>

    <!-- 커넥션 풀 라이브러리를 추가한다. spring boot 2의 경우는 hikariCP가 기본으로 사용된다.-->
    <dependency>
        <groupId>org.apache.commons</groupId>
        <artifactId>commons-dbcp2</artifactId>
        <version>2.6.0</version>
    </dependency>

    <!-- DataSource, Transaction등을 사용하려면 추가한다. spring-tx를 자동으로 포함시킨다.-->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</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>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>
    <finalName>securityexam</finalName>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-war-plugin</artifactId>
          <version>3.2.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
      </plugins>
    </pluginManagement>
  </build>
</project>

web.xml 파일을 대신하는 자바 Config 설정파일

Spring MVC를 사용할 경우 AbstractAnnotationConfigDispatcherServletInitializer를 상속받아 구현하면 편리하다.

WebAppInitializer.java

package org.edwith.webbe.securityexam.config;

import javax.servlet.*;

import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.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[]{"/"};
	}

	/*
       필터를 설정한다.
    */
    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter encodingFilter = new CharacterEncodingFilter();
        encodingFilter.setEncoding("UTF-8");

        return new Filter[]{encodingFilter};
    }
}

스프링 설정 파일

@ComponentScan을 이용해 dao와 service객체가 있는 패키지를 지정하고 있다.

해당 패키지에 @Repository나 @Service가 붙어있는 클래스가 있다면 자동으로 빈을 생성한다.

ApplicationConfig.java

package org.edwith.webbe.securityexam.config;

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

//레이어드 아키텍처에서 Controller가 사용하는 Bean들에 대해 설정을 한다.
//dao, service를 컴포넌트 스캔하여 찾도록 한다.
//어노테이션으로 트랜잭션을 관리하기 위해 @EnableTransactionManagement를 설정하였다.
@Configuration
@ComponentScan(basePackages = {"org.edwith.webbe.securityexam.dao", "org.edwith.webbe.securityexam.service"})
@Import({DBConfig.class})
public class ApplicationConfig {

}

DB 설정 파일

DBConfig.java

package org.edwith.webbe.securityexam.config;

import javax.sql.DataSource;

import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.TransactionManagementConfigurer;

@Configuration
@EnableTransactionManagement
@PropertySource("classpath:application.properties")
public class DBConfig implements TransactionManagementConfigurer{
	@Value("${spring.datasource.driver-class-name}")
    private String driverClassName;
    
    @Value("${spring.datasource.url}")
    private String url;
    
    @Value("${spring.datasource.username}")
    private String username;
    
    @Value("${spring.datasource.password}")    
    private String password;
    
    @Bean
	public DataSource dataSource() {
		BasicDataSource dataSource = new BasicDataSource();
		dataSource.setDriverClassName(driverClassName);
		dataSource.setUrl(url);
		dataSource.setUsername(username);
		dataSource.setPassword(password);
		return dataSource;
	}
    
	@Override
	public PlatformTransactionManager annotationDrivenTransactionManager() {
		return transactionManager();
	}
	
	@Bean
	public PlatformTransactionManager transactionManager() {
		return new DataSourceTransactionManager(dataSource());
	}
}

스프링MVC 설정 파일

스프링 MVC설정파일에는 @EnableWebMvc가 붙어있어야 한다.

@EnableWebMvc는 어노테이션 기반의 스프링 MVC를 구성할 때 필요한 Bean들을 자동으로 구성해주는 역할을 수행한다.

xml로 설정할 때는 <mvc:annontation-driven/>이라는 설정을 하게 되는데 같은 설정이라고 생각하면된다.

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.DefaultServletHandlerConfigurer;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.ViewResolverRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
@ComponentScan(basePackages = {"org.edwith.webbe.securityexam.controller"})
public class MvcConfig implements WebMvcConfigurer {
	// default servlet 핸들러를 설정한다.
	/* 원래 서블릿은 / (모든 요청)을 처리하는 default servlet을 제공한다. 스프링에서 설정한 path는 스프링이 처리하고, 스프링이 처리하지 못한 경로에 대한 처리는
	디폴트 서블릿에게 전달하여 처리하게 된다. */
	@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/");
	}
}

스프링 시큐리티를 사용하기 위한 설정 파일

AbstractSecurityWebApplicationInitializer를 상속받는 클래스가 있을 경우

스프링 시큐리티가 제공하는 필터들을 사용할 수 있도록 활성화 해준다.

SecurityWebApplicationInitializer.java

package org.edwith.webbe.securityexam.config;

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

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

스프링 시큐리티를 이용하여 로그인/로그아웃/인증/인가 등을 처리하기 위한 설정파일

@EnableWebSecurity가 붙어 있을 경우 스프링 시큐리티를 구성하는 기본적인 빈(Bean)들을 자동으로 구성해줍니다.
WebSecurityConfigurerAdapter를 상속받으면, 특정 메소드를 오버라이딩 함으로써 좀 더 손쉽게 설정할 수 있습니다.

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;

@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
	// /webjars/** 경로에 대한 요청은 인증/인가 처리하지 않도록 무시합니다.
	@Override
	public void configure(WebSecurity web) throws Exception{
		web.ignoring().antMatchers("/webjars/**");
	}
	
	// /와 /main에 대한 요청은 누구나 할 수 있지만, 그 외의 요청은 모두 인증 후 접근 가능합니다
	@Override
	protected void configure(HttpSecurity http) throws Exception{
		http.csrf().disable()
			.authorizeRequests()
			.antMatchers("/", "/main").permitAll()
			.anyRequest().authenticated();
	}
	
	//패스워드 인코더를 빈으로 등록합니다.
	//암호를 인코딩하거나, 인코딩된 암호와 사용자가 입력한 암호가 같은지 확인할 때 사용합니다.
	@Bean
	public PasswordEncoder endcoder() {
		return new BCryptPasswordEncoder();
	}
}

메소드 별로 분리해서 보자면,

첫번째 메소드,

@Override
public void configure(WebSecurity web) throws Exception{
	web.ignoring().antMatchers("/webjars/**");
}

<As-Is>
public void configure(WebSecurity web) 메소드를 오버라이딩 하는 이유는
인증/인가가 필요 없는 경로를 설정할 필요가 있을 때 오버라이딩을 합니다.

<To-Be>
public void configure(WebSecurity web) 메소드를 오버라이딩 하는 이유는
인증/인가가 필요 없는 경로를 설정할 필요가 있기 때문입니다.

해당 메소드는 스프링 시큐리티 설정이 자동으로 호출이 되는데, 이때 WebSecurity객체를 파라미터로 넣어주게 된다.

해당 객체의 ignoring()메소드에 무시될 경로를 지정하면 된다. 여기에는 "/webjars/**"을 설정하였다.

 

두번째 메소드,

// /와 /main에 대한 요청은 누구나 할 수 있지만, 그 외의 요청은 모두 인증 후 접근 가능합니다
@Override
protected void configure(HttpSecurity http) throws Exception{
	http.csrf().disable()
		.authorizeRequests()
		.antMatchers("/", "/main").permitAll()
		.anyRequest().authenticated();
}

protected void configure(HttpSecurity http) 메소드를 오버라이딩 한다는 것은 인증/인가에 대한 설정을 한다는 의미이다.

가장 중요한 메소드라고 말할 수 있다.

 http.csrf().disable() 는 csrf()라는 기능을 끄라는 설정이다.

csrf는 보안 설정 중 POST방식으로 값을 전송할 때 token을 사용해야하는 보안 설정이다.

csrf는 기본으로 설정되어 있는데 csrf를 사용하게 되면 보안성은 높아지지만 개발 초기에는 불편함이 있다는 단점이 있다.

그래서 csrf 기능을 끄도록 한 것이다. disable() 메소드는 http(HttpSecurity)를 리턴한다.

이말은 disable().authorizeRequests()는 http.authoriazeRequests()와 같은 의미를 가진다.

http.authorizeRequests()
    .antMatchers("/", "/main").permitAll()
    .anyRequest().authenticated();

위의 설정은 "/"와 "/main" 경로는 누구나 접근(permitAll)할 수 있도록 한 것이며,

그 외 경로는 인증을 한 후에만 접근할 수 있다는 것을 의미한다.

 

보안 설정 테스트를 위한 컨트롤러 클래스

기본적인 보안 설정을 했다면, 실제로 잘 동작하는지 테스트해봐야 할 것이다.

아래의 MainController는 2개의 path를 처리하고 있다.

참고로 @ResponseBody 어노테이션이 붙어 있을 경우에는 리턴하는 문자열을 화면에 직접 출력한다.

@ResponseBody 어노테이션이 없을 때는 View이름을 리턴하고 jsp를 사용한다.

 

2개의 path는 각각 "/main"과 "securepage" 이다.

앞에서 "/main"은 누구나 접근할 수 있다고 했다. "/"와 "/main"을 제외하면 모두 인증후에 사용한다고 설정되어 있다.

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 {
	@RequestMapping("/main")
	@ResponseBody
	public String main() {
		return "main page";
	}
	
	@RequestMapping("/securepage")
	@ResponseBody
	public String securitypage() {
		return "secure page";
	}
}

http://localhost:8080/securityexam/main 을 실행하면 아래의 그림과 같이 잘 표시되는 것을 알 수 있다.

http://localhost:8080/securityexam/securepage 를 요청해보면 응답 상태 코드가 403이 온 것을 알 수 있다.

응답 상태 코드가 403이 왔다는 것은 해당 경로는 인증을 거친 후에만 접근을 해야 한다는 것을 의미한다.

출처 : 부스트코스 백엔드 과정

https://www.boostcourse.org/

 

다 함께 배우고 성장하는 부스트코스

부스트코스(boostcourse)는 모두 함께 배우고 성장하는 비영리 SW 온라인 플랫폼입니다.

www.boostcourse.org

 

댓글