Spring Security 구조와 동작 방식

홍헌·2024년 4월 16일
0

spring

목록 보기
6/7

현재 Spring Boot로 토이 프로젝트를 진행하며 Spring Security를 이용해서 로그인 Restful api를 구현하고 있다. 프로젝트에서 남들이 해놨던 코드들만 보고 개념만 알고있었는데 이번 기회에 간단하게 정리를 해보려한다.
(이 글은 Spring Security 6.2.4 공식 문서를 참고해서 작성했으며, 더 자세한 내용을 원한다면 아래 링크에서 보면 될 것 같다.)
[Spring Security 6.2.4 공식 문서]https://docs.spring.io/spring-security/reference/servlet/architecture.html

Spring Security의 역할

Spring Security의 역할은 크게 3가지라 할 수 있다. 인증(Authentication), 인가(Authorization), 악용에 대한 보호(Protection Against Exploits)이고, 이를 통해 보안을 강화한다. 그리고 이러한 역할은 FilterChain을 이용한다.

FilterChain

FilterChain 이해를 위해 아래 그림을 보자.

필터는 해당 그림처럼 체인으로 되어있어 클라이언트의 요청에 따라 필터가 적용되며, 필터마다 다른 로직을 수행하게 만들 수 있다. 필터들을 거쳐 servlet에 도달하면 servlet은 controller로 요청을 보내 비지니스 로직을 수행하는 방식으로 동작한다. FilterChain의 역할은 그림에 있듯이 2가지이다.

1. Filter는 하위 필터 혹은 servlet이 수행되는 것을 방지

Filter는 우리가 아는 단어 의미대로 조건에 맞게 무언가를 걸러내는 역할이다. 따라서 HttpServletRequest가 조건에 맞게 들어오지 않을 경우, 하위에 있는 Filter 혹은 Servlet이 실행되지 않고 HttpServletResponse를 작성하여 요청을 막는 것이다.

2. HttpServletRequest 혹은 HttpServletResponse 가공

로그인을 예로 들자면 들어왔을 때, 클라이언트가 로그인을 하려고 할 때, 고객이 보낸 로그인 정보를 확인하고 맞으면 해당 정보를 service에서 response로 줄 것이다. 해당 response를 그대로 보내주는 것이 아니라 필터로 걸러서 토큰을 생성해 보내주는 것이다.

따라서 이러한 필터의 흐름과 역할에 대한 이해는 굉장히 중요하다.

SecurityFilterChain

filterChain에 있는 filter는 말 그대로 필터링만 해주는 역할을 한다. 즉, 어떤 행동을 할지는 구현하는 구현체들은 SecurityFilterChain안에 존재한다. 아래 그림과 내가 만들고 있는 SecurityFilterChain 코드를 예시로 보자.

@Configuration
@EnableWebSecurity
public class SecurityConfig {
    private final AuthenticationConfiguration authenticationConfiguration;
    private final JWTUtil jwtUtil;

    public SecurityConfig(AuthenticationConfiguration authenticationConfiguration, JWTUtil jwtUtil) {
        this.authenticationConfiguration = authenticationConfiguration;
        this.jwtUtil = jwtUtil;
    }


    //비밀번호 해시로 암호화
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    //AuthenticationManager Bean 등록
    @Bean
    public AuthenticationManager authenticationManager(AuthenticationConfiguration configuration) throws Exception{
        return configuration.getAuthenticationManager();
    }


    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        //jwt방식이라서 필요 없는 것들 끄기
        //csrf disable
        http
                .csrf((auth) -> auth.disable());
        //form 로그인 방식 disable
        http
                .formLogin((auth) -> auth.disable());
        //http basic 인증방식 disable
        http
                .httpBasic((auth) -> auth.disable());

        //경로별 인가 작업 설정
        http
                .authorizeHttpRequests((auth) -> auth
                        //URL 루트 설정
                        .requestMatchers("/login","/", "/join").permitAll()
                        //사용자 권한별 경로 설정
                        .requestMatchers("/admin").hasRole("ADMIN")
                        //다른 api 호출은 인가된 사람만 들어갈 수 있게 설정(테스트 후 경로별 변경 필요)
                        .anyRequest().authenticated()
                );
        //JWTFilter 등록
        http
                .addFilterBefore(new JWTFilter(jwtUtil),LoginFilter.class);
        //커스터마이즈 된 로그인 필터
        http
                .addFilterAt(new LoginFilter(authenticationManager(authenticationConfiguration), jwtUtil), UsernamePasswordAuthenticationFilter.class);
        //세션 설정
        //jwt방식이라서 stateless로 설정
        http
                .sessionManagement((session) -> session
                        .sessionCreationPolicy(SessionCreationPolicy.STATELESS));

        //빌더 타입 반환
        return http.build();
    }
}

필터 구현
서블릿 컨테이너에서 필터 등록 방법은 표준화 되어있다. 그러나 어떠한 구현체를 이용할 것인지는 알지 못하여 Spring에 정의된 Bean들을 기준으로 가져온다.
따라서 빈들을 Filter 역할을 할 수 있게 FilterChainProxy가 해당 필터 요청에 맞게 SecurityFilterChain안에서 빈을 가져오고, FilterChainProxy가 DelegatingFilterProxy 안에 Wrapping 된다.

필터 설정
필터를 설정하는 방법은 Servlet Filter에서 설정을 하거나 FilterChainProxy에서 설정하는 방법이 있다. 두 가지의 차이점은
Filter : URL을 기준으로 밖에 설정 불가
FilterChainProxy : URL, HTTP메서드, header 등을 기준으로 설정할 수 있다.
내 코드를 기준으로 현재 SecurityFilterChain에 설정해놨다. 그리고 requestMatchers로 URL을 기준으로 그리고 heade에 있는 role을 기준으로 필터를 걸고, addFilterBefore, addFilterAt으로 어떤 인스턴스를 제공할건지를 작성한 것이다. Servlet Filter에서 설정하고 싶으면 공식 문서의 예시를 확인하면 좋을 것 같다.

그림 및 글 출처

[Spring Security 6.2.4 공식 문서]https://docs.spring.io/spring-security/reference/servlet/architecture.html

0개의 댓글