BackEnd/Spring Security

[Spring Security] 내부 및 인증 동작 원리

코딩마루 2024. 9. 10. 18:29

1. 동작 원리

Spring Security 의존성이 없는 경우 'HTTP 요청 → WAS → 필터 → 서블릿 → 컨트롤러' 순으로 작동한다. 그림으로 표현하면 다음과 같다.

Spring Security 미적용

반면에, Spring Security 의존성을 추가하면 필터를 하나 추가하여 중간에 요청을 가로챈다. 가로채진 요청은 Spring Security 로직을 마친 후 다음 필터로 복귀한다.

Spring Security 적용

  • DelegatingFilterProxy: Bean Filter를 찾아 해당 필터로 요청을 넘긴다. (FilterChainProxy가 없다면 Bean Filter들을 DelegatingFilterProxy에 하나하나 등록해야 한다.)
  • FilterChainProxy: Spring Security가 제공하는 특별한 Bean Filter다. 우리는 이 FilterChainProxy 덕분에 SecurityFilterChain을 통해 많은 Filter instance들로 요청을 전달할 수 있다. (FilterChainProxy에 담긴 여러 필터들을 DelegatingFilterProxy에 등록해준다.)
  • SecurityFilterChain: 필터들의 묶음으로 생각하면 된다. SecurityFilterCahin들은 FilterChainProxy가 현재 요청에 적용해야할 필터를 결정할 때 사용된다. (SecurityFilterChain에 있는 Filter들은 DelegatingFilterProxy에 바로 등록되지 않고 먼저 FilterChainProxy에 등록된다. 이 등록된 SecurityFilterChain들을 FilterChainProxy가 DelegatingFilterProxy에 등록해주는 것 같다.)

SecurityFilterChain은 아래와 같이 여러 개일 수 있다.

여러 SecurityFilterChain 사용 가능

2. SecurityFilterChain 등록

SecurityFilterChain을 등록하기 위해서는 아래와 같이 SecurityFilterChain을 리턴하는 @Bean 메소드를 등록하면 된다. (여러 개도 가능하다.)

@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();
    }
}

FilterChainProxy는 여러 개의 SecurityFilterChain 중 하나를 선택하여 요청을 전달한다. 이때 기준은 아래와 같다.

  • 필터 체인에 대한 RequestMatcher 값 일치 여부
  • 등록 인덱스 순 (위에서 filterChain1이 filterChain2보다 먼저 등록되었기 때문에 filterChain1의 인덱스가 0이고 filterChain2의 인덱스가 1이다. 인덱스를 정하고 싶다면 @Order 어노테이션을 사용하면 된다.)

RequestMatcher 값은 아래와 같이 설정할 수 있다.

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

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

    return http.build();
}

이때 주의할 점은 securityMatchers()를 통해 설정하는 것이지, authorizeHttpRequest를 통한 인가 설정과는 다르다는 점을 주의해야 한다.

  • 추가적으로 @EnableWebSecurity(debug=true)를 사용하면 요청이 오는 경우 securityFilterChain의 필터 목록을 출력할 수 있다.

3. 필터 적용 제외 방법

굳이 필터를 적용하지 않아도 되는 요청의 경우 아래 코드를 활용해 제외할 수 있다. 이 경우 하나의 SecurityFilterChain이 생성되며 해당 필터 체인은 0번 인덱스로 설정된다.

@Bean
public WebSecurityCustomizer webSecurityCustomizer() {

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

4. 인증 아키텍처

4.1. 용어 정리

인증 과정을 알아보기 전에 Spring Security에서 사용하는 용어들을 알아보자.

  • Authentication(인증): 사용자가 본인인지 확인하는 절차
  • Authorization(인가): 인증된 사용자가 요청하는 자원을 사용할 권한이 있는지 확인하는 절차
  • Principal(접근 주체): 보호받는 자원에 접근하는 대상
  • Credential(비밀번호): 접근 주체의 비밀번호

Spring Security는 기본적으로 인증 후 인가를 진행하여, 인가 과정에서 리소스 접근 권한이 있는지 확인한다. 이러한 인증과 인가를 위해서 Spring Security는 Principal을 아이디, Credential을 비밀번호로 사용하는 Credential 기반의 인증 방식을 사용한다. 주요 모듈은 다음과 같다.

  • SecurityContextHolder: Spring Security가 인증된 사용자의 정보를 저장하는 공간이다.
  • SecurityContext: 인증된 사용자의 Authentication을 보관한다.
  • Authentication: 접근 주체의 인증 정보와 권한을 가진다.

4.2. 인증 과정

인증 과정

  1. 사용자가 로그인 정보가 담긴 Http 요청을 보낸다.
  2. AuthenticationFilter가 요청을 받으면 요청에 담긴 username과 password를 통해 UsernamePasswordAuthenticationToken(=Authentication object)을 만든다.
  3. 만들어진 AuthenticationToken을 AuthenticationManager에 전달한다. AuthenticationManager는 인터페이스고, 실제 구현체는 ProviderManager다. ProviderManager는 (유저 요청들을 인증하는데 사용되는) AuthenticationProvider들을 리스트형태로 가지고 있다.
  4. AuthenticationManager가 AuthenticationProvider(들)에게 인증을 요청한다.
  5. 몇 가지 AuthenticationProvider는 Username을 기반으로 UserDetails를 얻기 위해 UserDetailService를 사용한다. (UserDetailsService는 username을 통해 DB에서 사용자 정보를 가져온다.)
  6. 넘겨받은 사용자 정보로 UserDetails를 만든다. (UserDetails는 인터페이스고, 실제 구현체는 User인 것이다.)
  7. AuthenticationProvider(들)는 UserDetails를 받고 사용자 정보와 비교한다.
  8. 만약 인증에 성공했다면, 사용자 정보가 담긴 Authentication 객체를 반환한다. 실패한 경우 AuthenticationException을 던진다.
  9. AuthenticationManager는 받은 Authentication 객체를 Authentication Filter에 전달한다.
  10. AuthenticationFilter는 미래에 Authentication 객체를 SecurityContext에 저장한다.

출처:
https://docs.spring.io/spring-security/reference/

https://chathurangat.wordpress.com/2017/08/23/spring-security-authentication-architecture/#more-54