티스토리 뷰

서버 어플리케이션을 설계하고 개발하다보면 보안 이슈에 맞닥드리게 된다.

그러나 이러한 이슈들을 이미 수많은 개발자가 겪어온 이슈들이기에 Spring Security에서 제공하는 기능들을 파악하고 잘 활용하여 해결해나갈 수 있을 것이다.

이번 포스팅에서는 서버 어플리케이션을 설계할 때 고려해야할 사항들을 파악하고, 이에 대해 Spring Security에서는 기본적으로 어떠한 방식으로 해결하는지 알아보자.

 

Spring Security FilterChain Architecture

출처: 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에서는 Client의 요청을 DispatcherServlet이 처리하기 이전에 FilterChain의 Filter들을 거치게 된다.

우선 이를 테스트하기 위해 간단한 API를 개발했다.

@RestController
public class HelloController {

    @GetMapping("/api/v1/hello-world")
    public ResponseEntity<String> retrieveHelloWorld(String name) {
        return ResponseEntity.ok("HELLO " + name + " WORLD!");
    }
}

그리고 DispatcherServlet의 doService 메서드에 Breakpoint를 걸고 아래와 같이 요청해보았다.

더보기

http://localhost:8080/api/v1/hello-world?name=security

request에 대한 stack trace

위 스택 트레이스는 아래에서부터 보면 된다.

우선 ApplicationFilterChain이라는 FilterChain 구현 클래스의 doFilter 메서드와 internalDoFilter메서드가 실행된다.

그리고 OncePerRequestFilter의 doFilter 메서드가 실행되고, CharacterEncodingFilter의 doFilterInternal이 실행된다.

그리고 위와 동일한 로직이 반복되지만 최종적으로는 FormContentFilter, RequestContextFilter의 doFilterInternal이 실행된다.

Spring MVC 요청 처리 구조

공식 문서에서 제공하는 위와 같은 다이어그램과 비교하여 이해해보면..

우선 ApplicationFilterChain 인스턴스가 FilterChain 타입의 빈 객체로 초기화 될 것이다.

그리고 이 ApplicationFilterChain 인스턴스 내부적으로 Filter 배열을 가지며, 이 때의 Filter들이 CharacterEncodingFilter, FormContentFilter, RequestContextFilter 등일 것이다.

그리고 클라이언트로부터 요청을 받게 되면 이 필터들에 정의된 로직을 실행시키게 된다.

 

DelegatingFilterProxy

위 스택 트레이스에서 DelegatingFilterProxy를 볼 수 있었을 것이다.

해당 클래스에서는 내부적으로 FilterChainProxy의 로직을 수행하며, 그 내부적으로는 SecurityFilterChain의 필터들의 로직을 수행하게된다.

FilterChainProxy.doFilterInternal(...) 부터 스택 트레이스

위 스택 트레이스를 보면, WebAsyncManagerIntegrationFilter, SecurityContextHolderFilter, HeaderWriterFilter, CorsFilter, CsrfFilter, LogoutFilter, DefaultLoginPageGenerateFilter 등 보안과 관련된 필터들의 로직이 수행된다.

 

Spring Security가 인증을 처리하는 방법

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

 

Servlet Authentication Architecture :: Spring Security

ProviderManager is the most commonly used implementation of AuthenticationManager. ProviderManager delegates to a List of AuthenticationProvider instances. Each AuthenticationProvider has an opportunity to indicate that authentication should be successful,

docs.spring.io

인증(Authentication)이란 서버에 들어온 요청이 올바른 사용자로부터 출발한 것인지 증명하는 과정이다.

Spring Security는 여러 클래스의 협업을 통해 인증을 처리하기 때문에 이 과정을 이해하기 위해서는 각 클래스의 역할을 파악할 필요가 있다.

SecurityContextHolder

Spring Security에서 인증받은 사용자의 정보가 저장되는 공간이다

SecurityContext

SecurityContextHolder에서 획득되며 현재 인증된 사용자의 Authentication 인스턴스를 포함한다.

Authentication

인증을 위해 사용자가 입력한 자격증명이 Authentication 타입으로 AuthenticationManager로 입력되거나, SecurityContext에서 획득하여 AuthenticationManager로 입력된다.

GrantedAuthority

인증을 위해 사용자가 입력한 자격증명이 Authentication 타입으로 AuthenticationManager로 입력되거나, SecurityContext에서 획득하여 AuthenticationManager로 입력된다.

AuthenticationManager

스프링 시큐리티의 필터가 인증을 어떻게 수행할 것인지 정의하는 API

ProviderManager

AuthenticationManager의 일반 구현체

AuthenticationProvider

ProviderManager가 특정 인증을 수행할 때 사용

 

Spring Security에서 인증된 사용자의 정보는 SecurityContextHolder에 저장된다.

SecurityContextHolder

SecurityContextHolder는 위와 같은 구조를 가진다.

SecurityContextHolder는 기본적으로 ThreadLocal을 사용하여 사용자 정보를 가져온다. 따라서 사용자 정보에 대한 정보를 따로 파라미터로 넘기지 않아도 된다.

SecurityContextHolder의 initializeStrategy() 메서드

아무런 설정없이 해당 initializeStrategy() 메서드에 BreakPoint를 찍어봤더니 strategyName이 null로 들어왔다.

그렇게 되면 strategyName이 "MODE_THREADLOCAL"로 세팅되고 이에 따라 strategy 변수는 ThreadLocalSecurityContextHolderStrategy 인스턴스로 초기화된다.

 

SecurityContextHolder의 getContext() 메서드

그렇게 되면 SecurityContextHolder의 getContext() 메서드는 ThreadLocalSecurityContextHolderStrategy의 getContext() 메서드의 결과를 반환한다.

ThreadLocalSecurityContextHolderStrategy 구현 내용

그렇다면 ThreadLocalSecurityContextHolderStrategy의 구현 내용을 살펴보면, ThreadLocal 인스턴스로 초기화된 상수가 있고 이 상수를 기준으로 context를 반환한다.

 

이렇게 반환한 SecurityContext 내부에는 Authentication 객체가 들어있을 것이다.

 

Authentication 객체는 두 가지 역할을 수행한다.

1. 사용자가 자격증명을 AuthenticationManager로 전달하기 위해 입력하는 타입

2. 이미 인증된 사용자에 대한 정보를 포함하여 SecurityContextHolder에서 획득

 

그리고 세 가지 정보를 포함하고 있는데 이 정보들은 아래와 같다.

1. principal: 사용자 식별 정보

2. credentials: 암호

3. authorities: 사용자 권한 혹은 역할

 

ProviderManager는 AuthenticationManager의 구현체로 authenticate(Authentication authentication) 메서드를 구현한다.

ProviderManager

그리고 내부적으로 보면 여러 개의 AuthenticationProvider를 반복적으로 수행한다.

ProviderManager 내부로직

 

그렇다면 아래와 같이 설정하여 기본적인 username/password 기반의 인증 프로세스를 구성해보자.

@Configuration
public class SecurityConfig {

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        return http
                .authorizeHttpRequests((authorize) -> authorize.anyRequest().authenticated())
                .httpBasic(Customizer.withDefaults())
                .formLogin(Customizer.withDefaults())
                .build();
    }

    @Bean
    public UserDetailsService userDetailsService() {
        UserDetails userDetails = User
                .withUsername("user")
                .password("{noop}password")
                .roles("USER")
                .build();

        return new InMemoryUserDetailsManager(userDetails);
    }
}

위의 설정에 따르면 모든 요청에 대해서 인증이 필수적이다.

이 때 필요한 인증정보를 인메모리에 저장해서 가지고 있을 것이며, 인증되지 않은 요청은 login form에 의해 인증을 받도록 한다.

 

 

Spring Security가 인가를 처리하는 방법

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

 

Authorization Architecture :: Spring Security

It is a common requirement that a particular role in an application should automatically "include" other roles. For example, in an application which has the concept of an "admin" and a "user" role, you may want an admin to be able to do everything a normal

docs.spring.io

 

공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
TAG
more
«   2025/08   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
31
글 보관함