본문 바로가기
백엔드

[Spring Boot] Spring Security 내부 구조 파악하기

by 육빔 2024. 8. 22.
728x90
반응형

Spring Security를 사용할때 단순히 의존성 넣고 다른 예시 코드를 따라치는 식으로 진행했었는데 내부 구조는 전혀 모르고 사용했었어서 정리하고자 한다.

 

https://docs.spring.io/spring-security/reference/servlet/architecture.html

 

Architecture :: Spring Security

The Security Filters are inserted into the FilterChainProxy with the SecurityFilterChain API. Those filters can be used for a number of different purposes, like authentication, authorization, exploit protection, and more. The filters are executed in a spec

docs.spring.io

 

 

Spring Security 3.2.x 기준으로 진행했다.

 

시큐리티 의존성 없는 경우

클라이언트의 요청은 서버컴퓨터의 WAS의 필터를 통과하고 바로 스프링 컨테이너로 들어오게 된다.

 

시큐리티 의존성 있는 경우

WAS의 필터에 하나의 필터를 넣고 필터에서 요청을 가져감 -> 시큐리티 로직을 통과하고 다시 WAS의 다음 필터로 이동.

 

DelegatingFilterProxy에서 시큐리티 로직으로 이동하고 그 로직 안에는 여러개의 CSRF, login, logout, Auth 등 다양한 작업을 수행한다.

 

DelegatingFilterProxy

스프링 Bean을 찾아 요청을 넘겨주는 서블릿 필터

FilterChainProxy

스프링 시큐리티 의존성을 추가하면 DelegatingFilterProxy에 의해 호출되는 SecurityFilterChain들을 들고 있는 Bean

SecurityFilterChain

스프링 시큐리티 필터들의 묶음으로 실제 시큐리티 로직이 처리되는 부분, FilterChainProxy가 SecurityFilterChain들을 들고 있다.

 

 

기본적으로 시큐리티 의존성을 추가하면 DefaultSecurityFiterChain이 생성된다. 그래서 우리가 처음에 설치하고 사이트에 접속하려고 하면 로그인 폼 창이 뜨는 것이 기본 필터 체인 때문이다. 그리고 우리가 사용을 할때의 코드를 보자

 

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    
    @Bean
    public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception{
        
        return http.build();
    }
}

 

 

기본적으로 SecurityFilterChain을 등록하는 방법이다. 

필요에 따라 여러개의 SecurityFilterChain을 등록 할 수 있다.

 

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    @Bean
    public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception{

        return http.build();
    }

    @Bean
    public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {

        return http.build();
    }
}

 

이렇게 등록한 후 WAS에서 요청이 온 후 FIlterChainProxy에서 가로챈 후 N개의 SecurityFilterChain 중 하나를 선택해서 요청을 전달해야 하는데 기준은 다음과 같다.

 

1. 등록 인덱스 순

2. 필터 체인에 대한 RequestMatcher 값이 일치하는지 확인

 

이런 기준으로 다음과 같은 문제가 발생할 수 있다.

@Bean
public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/user").permitAll());

    return http.build();
}

@Bean
public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/admin").permitAll());

    return http.build();
}

 

/admin 경로로 요청을 보내면 인증과정 없이 들어갈 것으로 생각하지만 시큐리티의 매핑 자체가 없어 잘못된 응답이 발생한다.

filterChain1이 filterChain2보다 먼저 등록되어 있기 때문.

경로 설정을 하지 않아서 /** 경로에 대해 반응

 

@Bean
public SecurityFilterChain filterChain1(HttpSecurity http) throws Exception {

    http
            .securityMatchers((auth) -> auth.requestMatchers("/user"));

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/user").permitAll());

    return http.build();
}

@Bean
public SecurityFilterChain filterChain2(HttpSecurity http) throws Exception {

    http
            .securityMatchers((auth) -> auth.requestMatchers("/admin"));

    http
            .authorizeHttpRequests((auth) -> auth
                    .requestMatchers("/admin").authenticated());

    return http.build();
}

 

이런식으로 securityMatchers로 경로 매핑을 진행해준다.

 

또한 이미지, 파일 정적 자원의 경우 필터를 통과하지 않도록 아래 구문을 넣어준다.

 

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {

    return web -> web.ignoring().requestMatchers("/img/**");
}

 

 

ref

https://www.devyummi.com/

 

개발자 유미 | 커뮤니티

 

www.devyummi.com

728x90
반응형