🍳

초보 개발자의 일상

Java/SpringBoot

@Configuration, @Bean, @EnableWebSecurity, SecurityFilterChain, HttpSecurity

dev.jelee 2024. 10. 28. 15:43

생각

spring security에 대해서 수업을 듣다가 사용자 권한에 들어가면서 특정 클래스에 @Configuration 어노테이션을 정의해줬다. 그래서 이게 정확히 무엇인지 궁금했다.

그리고 찾아보니 @Bean과 같이 사용되는데 각 Bean의 인스턴스는 컨테이너 내에서 하나만 존재 한다는 설명을 듣고 이해가 안 갔다. 하나만 존재한다는 말이 무슨 말인지 이해가 가지 않아서 관련 내용을 더 찾아보았다.


@Configuration, @Bean,
@EnableWebSecurity,
SecurityFilterChain, HttpSecurity의 중요성

  • Spring Security에서 보안 관련 기능을 추가하려면 SecurityFilterChain 인터페이스를 사용해야한다.
  • 그리고 SecurityFilterChain 인터페이스를 사용하기 위해서@Configuration@EnableWebSecurity 어노테이션을 사용하여 보안 설정 클래스를 만들어야한다. 그래야지 필터 체인을 정의하고 필요한 보안 설정을 추가할 수 있다.

@Configuration

  • Java 기반의 설정 클래스를 정의하는 데 사용된다.
  • 이 어노테이션이 붙은 클래스는 Spring의 애플리케이션 컨텍스트에서 Bean을 정의하고 설정할 수 있다.
  • @Configuration이 붙은 클래스 안에는 @Bean 어노테이션이 붙은 메소드가 있을 수 있다. 이메소드들은 Spring 컨테이너에 의해 관리되는 Bean을 정의한다.
  • Bean을 수동으로 등록하기 위해서 사용한다.
  • Bean을 등록할 때 싱글톤(singleton)이 되도록 보장해준다.
  • SecurityFilterChain 인터페이스 타입의 클래스를  생성할 때  @Configuration을 설정하면 스프링 설정 파일처럼 동작하게 만든다.

@Bean

  • 주로 객체를 생성하고 스프링 컨테이너에 등록하여, 애플리케이션 전반에서 쉽게 사용할 수 있도록 할 때 사용된다. 또한 의존성을 관리하고, 초기화 및 소멸 로직을 정의하는 데 유용하다.

@Configuration에서 사용되는 @Bean은 아래에 정리해보았다.

  • 여러 @Bean 메소드
    • @Configuration이 설정된 클래스 안에는 여러개의 @Bean이 존재할 수 있다. 이 메소드들은 각각 다른 타입의 Bean을 반환하거나 같은 타입의 Bean을 반환할 수 있다.
  • 싱글톤 관리
    • @Bean 메소드가 반환하는 인스턴스는 각각 하나만 존재한다.
    • 각 @Bean 메소드에서 생성된 인스턴스는 Spring 컨테이너에 의해 관리된다.
    • 그리고 해당 Bean은 애플리케이션이 실행되는 동안 하나의 인스턴스만 존재한다는 말이다. 하나의 인스턴스만 존재한다는 말은 해당 @Bean이 속한 클래스에서 유일하게 존재해야 한다는 의미이다.
    • 각 인스턴스가 애플리케이션 전반에 걸쳐 재사용되며, 상태를 공유한다. 즉, 이 인스턴스의 상태가 변경되면 모든 사용자가 그 변경된 상태를 참조하게 된다.

예시 - @Configuration에서 사용되는 @Bean 예시

// controller 예시
@Configuration
public class AppConfig {

    @Bean
    public MyService myService() {
        return new MyService();
    }

    @Bean
    public MyRepository myRepository() {
        return new MyRepository(myService()); // myService() 호출로 의존성 주입
    }
}

 

  • @Configuration 어노테이션 안에 @Bean이 있다.
  • 각각의 @Bean 메소드는 유일한 인스턴스를 반환한다. 이 인스턴스는 내부 상태를 가질 수 있어 값이 변경될 수 있다.

@EnableWebSecurity

  • Spring Security에서 웹 보안을 활성화하는 어노테이션.
  • @EnableWebSecurity 어노테이션이 붙은 클래스는 웹 보안 구성을 위한 설정 파일임을 나타내고, Spring Secuity의 기본 웹 보안 기능이 이 클래스에서 활성화된다.
  • 내부적으로 여러 필터들이 자동으로 설정되어 인증(authentication), 권한 부여(authorization) 등을 처리하는 필터 체인을 구성하게 된다.
  • HTTP 요청에 대한 보안 처리가 가능해지며, 여기서 작성한 보안 설정(HttpSecurity를 통해 정의한 설정 등)이 적용된다.

SecurityFilterChain

  • 클라이언트의 모든 HTTP 요청을 필터링하는 보안 필터 체인.
  • Spring Security에서 보안 필터의 연쇄(chain)를 구성하는 인터페이스다.
  • Spring Security의 보안 필터들은 SecurityFilterChain에 의해 구성되며, 요청이 들어오면 이 필터 체인을 거쳐서 인증과 권한 검사가 수행한다.
  • 필터 체인에는 여러 보안 관련 필터들이 연결되어 있는데, 예를 들어 UsernamePasswordAuthenticationFilter, CsrfFilter, SessionManagementFilter 등이 포함되어 있다.
  • 클라이언트 요청이 들어오면 SecurityFilterChain이 먼저 그 요청을 가로채 필터 체인 내의 필터들이 요청에 대해 하나씩 인증, 권한 부여 등의 작업을 수행한다.
    • 예를들어 HttpSecurity 클래스를 사용하여 필터 체인에 어떤 필터를 넣을지, 어떤 보안 규칙을 적용할지 정의한다.

예시 - SecurityFilterChain과 함께 사용되는 @Configuration 예시

@Configuration
@EnableWebSecurity
public class SecurityConfig {
	
	@Bean
	public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
		
		log.info("security config ...");
		
		http
		.formLogin(formLogin -> formLogin
	        .loginPage("/login")
	        .defaultSuccessUrl("/", true)
	        .permitAll()
				)
		.authorizeHttpRequests(authorize -> authorize
	      .requestMatchers("/", "/home", "/signup").permitAll()
	      .requestMatchers("/users", "/user/*/roles", "/user/*/role/**").hasRole("ADMIN")
        .anyRequest().authenticated()
				)
		.logout(logout -> logout
    		.logoutSuccessUrl("/")
    		.permitAll()
    		);
		
		
		return http.build();

	}
	
	@Bean
	public PasswordEncoder passwordEncoder() {
		return new BCryptPasswordEncoder();
	}
	
}

 

 

SecurityFilterChain 동작 순서

  1. 요청 수신: 클라이언트가 서버에 HTTP 요청을 보낸다.
  2. SecurityFilterChain 호출: Spring Security가 요청을 가로채고, 등록된 SecurityFilterChain을 통해 요청을 처리한다.
  3. 필터 순차 실행: SecurityFilterChain 내의 필터들이 등록된 순서대로 실행된다. 일반적으로 다음과 같은 주요 필터들이 포함된다:
    • SecurityContextPersistenceFilter: 요청에 대한 SecurityContext를 로드하고, 인증 정보를 저장한다.
    • UsernamePasswordAuthenticationFilter (또는 다른 인증 필터): 사용자가 로그인 시 인증 정보를 확인한다. 로그인 폼에서 제출된 사용자 이름과 비밀번호를 검사한다.
    • BasicAuthenticationFilter: HTTP Basic 인증을 처리한다.
    • BearerTokenAuthenticationFilter: JWT와 같은 Bearer 토큰 인증을 처리한다.
    • FilterSecurityInterceptor: 요청에 대한 인가(authorization)를 수행한다. 인증된 사용자가 해당 자원에 접근할 수 있는지 검사한다.
    • ExceptionTranslationFilter: 보안 관련 예외를 처리하고, 적절한 응답을 생성한다.
  4. 인증 및 인가 처리:
    • 각 필터는 설정된 규칙에 따라 요청을 처리하며, 인증되지 않은 경우 로그인 페이지로 리다이렉트하거나 에러 응답을 보낸다.
    • 인증된 사용자인 경우, 요청이 계속 진행되며, 다음 필터로 넘어간다.
  5. 요청 전달: 모든 필터가 성공적으로 처리한 후, 최종적으로 요청이 실제 핸들러(컨트롤러)로 전달되어 비즈니스 로직이 실행된다.
  6. 응답 처리: 비즈니스 로직이 실행된 후, 응답이 생성되어 클라이언트에게 반환된다. 이 과정에서 추가적으로 처리할 필터가 있을 경우, 응답이 돌아오는 과정에서도 필터들이 실행될 수 있다.

HttpSecurity

  • HTTP 요청에 대한 보안 설정을 정의하는 클래스.
  • SecurityFilterChain이 필터를 통해 인증과 권한 검사를 하는데, 이 필터 체인에서 어떤 요청에 어떤 보안 규칙이 적용될지는 HttpSecurity에서 설정된다.
  • 예를 들어, 로그인 페이지 경로, 인증이 필요한 URL, 접근 권한 부여 등을 설정할 수 있다.
  • 사용자 요청이 필터 체인으로 들어온 경우
    • HttpSecurity에서 설정한 규칙들이 적용된다. 예를 들어, 특정 경로(/login, /home)는 인증 없이 접근이 가능하고, 그 외의 경로는 인증이 필요하다고 설정한 경우, 이 설정이 필터 체인에 반영되어 동작한다.
    • 인증이 필요할 경우, formLogin()으로 설정한 로그인 페이지로 리다이렉트된다.

HttpSecurity 클래스가 제공하는 메서드

1. authorizeHttpRequests()

  • 요청에 대한 권한을 설정합니다.
  • URL 패턴에 따라 접근 제어를 설정할 수 있다.
  • * Spring Security 5.0 이후 버전에서는 authorizeRequests() 대신 authorizeHttpRequests() 메서드를 사용

2. formLogin()

  • 기본 로그인 폼을 활성화한다.
  • 커스터마이징할 수 있는 메서드들이 제공된다 (예: loginPage(), permitAll()).

3. logout()

  • 로그아웃 관련 설정을 구성한다.
  • 로그아웃 URL, 로그아웃 성공 후 이동할 URL 등을 설정할 수 있다.

4. csrf()

  • CSRF(Cross-Site Request Forgery) 보호를 설정한다.
  • 기본적으로 활성화되어 있지만, 필요에 따라 비활성화할 수 있다.

5. httpBasic()

  • HTTP Basic 인증을 활성화한다.
  • 사용자가 브라우저에서 사용자 이름과 비밀번호를 입력할 수 있도록 한다.

6. sessionManagement()

  • 세션 관리 정책을 설정한다.
  • 최대 세션 수, 세션 고유성 등을 관리할 수 있다.

7. exceptionHandling()

  • 인증 및 권한 부여 실패 시 처리 방법을 정의한다.
  • 사용자 정의 오류 페이지를 설정할 수 있다.

8. cors()

  • CORS(Cross-Origin Resource Sharing) 설정을 구성한다.
    외부 도메인에서의 요청 허용 여부를 설정할 수 있다.

예시 - HttpSecurity 클래스의 메서드의 사용 방법

// 예시
@Override
protected void configure(HttpSecurity http) throws Exception {
    http
        .authorizeHttpRequests() // HTTP 요청에 대한 권한 설정
            .antMatchers("/public/**").permitAll() // 공용 경로 접근 허용
            .anyRequest().authenticated() // 나머지 요청은 인증 필요
        .and()
        .formLogin() // 로그인 폼 설정
            .loginPage("/login") // 커스텀 로그인 페이지
            .permitAll() // 모든 사용자에게 로그인 페이지 접근 허용
        .and()
        .logout() // 로그아웃 설정
            .logoutUrl("/logout")
            .logoutSuccessUrl("/"); // 로그아웃 성공 후 이동할 URL
}

헷갈리는 SecurityFilterChain, HttpSecurity 의 관계

쉽게 설명하자면 HttpSecurity가 "어떤 사용자만 특정 URL에 접근할 수 있다"라는 규칙을 설정했다고 하자. 그러면 이 설정을 통해 인증, 인가, 로그인 방식 등을 구체적으로 지정해줘야한다. 그런 다음 SecurityFilterChain이 HttpSecurity에서 설정한 규칙을 실제로 적용하는 필터 체인을 생성한다. 그래서 클라이언트로부터 요청이 들어오면 이 체인이 정의된 규칙에 따라 요청을 처리하는 역할을 한다.

요약하자면 HttpSecurity는 보안 설정을 만듦. SecurityFilterChain은 HttpSecurity가 만든 설정이 원활하게 이루어지도록 동작해줌.


예시를 살펴보면 .and() 가 있는 이유는..?

1. 가독성

설정이 체계적으로 구성되어 코드를 읽기 쉽게 만든다. 각 보안 설정이 블록으로 구분되어 있어 어떤 설정이 어떤 기능을 하는지 한눈에 알 수 있다.

2. 유연성

설정을 추가하거나 수정할 때, 코드의 흐름을 변경하지 않고도 쉽게 수정할 수 있다. 새로운 설정을 추가할 때 기존 구조를 유지할 수 있다.

3. 명확한 블록 구분

각 설정 블록의 종료를 명확히 하여, 설정 간의 관계를 이해하기 쉽게 해준다. 이는 특히 복잡한 보안 정책을 설정할 때 유용하다.

4. 기능 확장성

필요에 따라 설정을 더 추가하거나 커스터마이즈할 수 있는 유연성을 제공한다. 예를 들어, 다양한 인증 방식이나 접근 제어 로직을 간편하게 추가할 수 있다.

.and()의 역할

  • Spring Security의 HttpSecurity 설정에서 메서드 체이닝을 위한 방법으로 사용된다. 
  • 이 메서드는 설정을 더 구조화하고 읽기 쉽게 만들어 주는 역할을 한다.
    • 체이닝: HttpSecurity의 설정 메서드를 연속해서 호출할 수 있도록 해준다. 이를 통해 각 설정 블록을 구분하면서도 가독성을 높일 수 있다.
    • 블록의 종료: 특정 설정 블록이 끝났음을 나타내며, 이후 다른 설정을 추가할 수 있는 공간을 제공한다. 예를 들어, authorizeHttpRequests() 설정이 끝나고 나서 로그인 설정이나 로그아웃 설정을 이어서 할 수 있게 해준다.

 

 

참고