Spring Security 흐름 및 컴포넌트

jungseo·2023년 7월 12일
0

Spring

목록 보기
22/23

1. 웹 요청 처리 흐름


1. 사용자가 보호된 리소스 요청
2. 인증 관리자가 사용자 크리덴셜 요청
3. 사용자가 인증 관리자에게 크리덴셜 제공
4. 인증 관리자가 크리덴셜 저장소에서 사용자의 크리덴셜 조회
5. 인증 관리자는 사용자가 제공한 크리덴셜과 크리덴셜 저장소에 저장된 크리덴셜을 비교해 검증
6. 유효한 크리덴셜이 아닌 경우 예외 throw
7. 유효한 크리덴셜일 경우 접근 결정 관리자가 용자가 적절한 권한을 부여받았는지 검증
8. 적절한 권한을 부여받지 못한 사용자일 경우 예외 throw
9. 적절한 권한을 부여받은 사용자일 경우 리소스 접근 허용

1) 서블릿 필터 / 필터 체인

  • 사용자의 웹 요청이 Controller 같은 엔드포인트를 거쳐 접근하려는 리소스에 도달하기 전 인증 관리자나 접근 결정 관리자 같은 컴포넌트가 중간에 웹 요청을 가로채 사용자의 크리덴셜과 접근 권한을 검증

  • 서블릿 필터가 애플리케이션의 엔드포인트에 요청이 도달하기 전에 중간에서 요청을 가로챈 후 어떤 처리를 할 수 있는 적절한 포인트를 제공

  • javax.servlet.Filter 인터페이스를 구현한 서블릿 필터는 웹 요청(request)을 가로채어 전처리를 할 수 있으며,
    엔드포인트에서 요청 처리가 끝난 후 전달되는 응답(reponse)을 클라이언트에게 전달하기 전에 후처리 가능

  • 서블릿 필터는 하나 이상의 필터들을 연결해 필터 체인(Filter Chain)을 구성 가능

  • 서블릿 필터는 각각의 필터들이 doFilter()라는 메서드를 구현해야 하며, doFilter() 메서드 호출을 통해 필터 체인을 형성

  • 서블릿 필터에서 작업을 수행한 뒤, HttpServlet을 거쳐 DispatcherServlet에 요청이 전달되며,
    반대로 DispatcherServlet에서 전달한 응답에 대해 작업 수행 가능

2) Spring Security 필터 역할

  • DelegatingFilterProxy와 FilterChainProxy 클래스는 Filter 인터페이스를 구현 -> 서블릿 필터의 역할
  • Spring Security도 Spring의 ApplicationContext를 이용
  • 서블릿 필터와 연결되는 Spring Security의 필터를 ApplicationContext에 Bean으로 등록한 후에 이 Bean들을 이용해서 보안과 관련된 작업을 처리

(1) DelegatingFilterProxy

  • Bean으로 등록된 Spring Security의 필터를 사용하는 시작점
  • 보안과 관련된 어떤 작업을 처리하는 것이 아닌 서블릿 컨테이너 영역의 필터와 ApplicationContext에 Bean으로 등록된 필터들을 연결

(2) FilterChainProxy

  • 보안 작업을 처리하는 필터의 모음
  • Filter Chain은 URL 별로 여러 개 등록가능
  • FilterChainProxy가 어떤 Filter Chain을 사용할지 결정, 가장 먼저 매칭된 Filter Chain을 실행
    • 가장 먼저 매칭되는 패턴과 일치하는 Filter Chain만 실행
    • 매칭되는 Filter Chain이 없는 경우 디폴트 패턴 "/**" 패턴의 Filter Chain 실행

3) Filter, FilterChain 구현

  • Filter Chain은 여러 개의 Filter가 체인을 형성하고 있는 Filter의 묶음
  • Servlet FilterChain은 요청 URI path를 기반으로 HttpServletRequest를 처리
  • 애플리케이션에 요청을 전송하면 서블릿 컨테이너는 요청 URI의 경로를 기반으로 어떤 Filter와 어떤 Servlet을 매핑할지 결정
  • Filter는 Filter Chain 안에서 순서를 지정할 수 있으며 지정한 순서에 따라서 동작
  • Filter 클래스에 @Order() 애너테이션 사용 혹은
    Orderd 인터페이스를 구현해 Filter의 순서 지정 가능
  • FilterRegistrationBean을 이용해 Filter의 순서를 명시적으로 지정

FirstFilter 클래스

@Order(1) // (1)
public class FirstFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        System.out.println("# First Filter init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("# First Filter doFilter 시작");
        chain.doFilter(request, response);
        System.out.println("# First Filter doFilter 종료");
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
        System.out.println("# First Filter destroy");
    }
}
  • (1) @Order() 애너테이션으로 순서 지정 가능
  • Filter 인터페이스 구현 및 메서드 바디 정의

SecondFilter 클래스

@Order(2)
public class SecondFilter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
        System.out.println("# Second Filter init");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        System.out.println("# Second Filter doFilter 시작");
        chain.doFilter(request, response);
        System.out.println("# Second Filter doFilter 종료");
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
        System.out.println("# Second Filter destroy");
    }
}

Filter 적용을 위한 FilterConfiguration 구성

@Configuration
public class FilterConfiguration {
    @Bean
    public FilterRegistrationBean<FirstFilter> firstFilterRegister() {
        FilterRegistrationBean<FirstFilter> registrationBean = new FilterRegistrationBean<>(new FirstFilter());
//        registrationBean.setOrder(1); // (1)
        return registrationBean;
    }
    @Bean
    public FilterRegistrationBean<SecondFilter> secondFilterRegister() {
        FilterRegistrationBean<SecondFilter> registrationBean = new FilterRegistrationBean<>(new SecondFilter());
//        registrationBean.setOrder(2);
        return registrationBean;
    }
}
  • Filter 클래스에 @Order() 애너테이션 혹은 (1) 처럼 순서 지정 가능

실행시 출력 로그

# Second Filter init
# First Filter init

# First Filter doFilter 시작
# Second Filter doFilter 시작

# Second Filter doFilter 종료
# First Filter doFilter 종료

Filter 순서 지정 시 init() 메서드와 destroy() 메서드는 영향을 받지 않는다.


2. 인증 처리 흐름

ID/Password를 이용한 로그인 인증 방식의 인증 처리 흐름

  • (1) 사용자가 로그인 폼 등을 이용해 Username과 Password를 포함한 request 전송
  • (2) UsernamePasswordAuthenticationFilter가 요청을 전달
    • 전달 받은 Username과 Password를 가지고 UsernamePasswordAuthenticationToken 생성
      • UsernamePasswordAuthenticationToken은 Authentication 인터페이스의 구현 클래스
        • 현재 Authentication은 아직 인증 되지 않은 Authentication
  • (3) UsernamePasswordAuthenticationFilter가 Authentication을 AuthenticationManager에 전달
    • AuthenticationManager는 인증 처리를 총괄하는 매니저 역할의 인터페이스
      • AuthenticationManager를 구현한 클래스가 ProviderManager
        • ProviderManager가 실질적으로 인증 처리를 총괄
        • 직접 인증을 처리하지 않고 인증 처리를 AuthenticationProvider에 위임
  • (4) ProviderManager가 AuthenticationProvider에 Authentication 전달
  • (5) AuthenticationProvider가 UserDetailsService를 이용해 UserDetails 조회
    • UserDetails는 DB에 저장된 사용자의 Username과 Password(Credential), 사용자의 권한 정보를 포함
  • (6) UserDetailsService는 데이터베이스 등의 저장소에서 사용자의 크리덴셜(Credential)을 포함한 사용자의 정보를 조회
  • (7) UserDetailsService는 조회한 사용자의 크리덴셜을 포함한 사용자 정보를 기반으로 UserDetails 생성
  • (8) 생성한 UserDetails를 AuthenticationProvider에게 전달
  • (9) AuthenticationProvider는 PasswordEncoder로 UserDetails에 포함된 암호화된 Password와 Authentication에 포함된 Password가 매치되는지 검증 후 인증된 Authentication을 생성
  • (10) AuthenticationProvider가 인증된 Authentication을 ProviderManager에게 전달
  • (11) ProviderManager가 인증된 Authentication을 UsernamePasswordAuthenticationFilter에 전달
  • (12) UsernamePasswordAuthenticationFilter가 SecurityContextHolder를 이용해 SecurityContext에 인증된 Authentication을 저장

    SecurityContext는 이후에 Spring Security의 세션 정책에 따라서 HttpSession에 저장되어 사용자의 인증 상태를 유지하기도 하고, HttpSession을 생성하지 않고 무상태를 유지하기도 함


3. 권한 부여 흐름

  • AuthorizationFilter :
    Spring Security Filter Chain에서 URL을 통해 사용자의 액세스를 제한하는 권한을 부여하는 Filter
  • (1) AuthorizationFilter가 SecurityContextHolder로부터 Authentication을 획득
  • (2) 획득한 Authentication과 HttpRequest를 AuthorizationManager에게 전달
    • AuthorizationManager : 권한 부여를 총괄하는 인터페이스
  • (3) RequestMatcherDelegatingAuthorizationManager 내부에 매치되는 Authorization 구현 클래스가 있다면 해당 클래스가 사용자의 권한을 체크
    • RequestMatcherDelegatingAuthorizationManager
      • AuthorizationManager의 구현체 중 하나
      • RequestMatcher 평가식으로 매치되는 AuthorizationManager 구현체에 권한 부여 처리를 위임
  • (4) 적절한 권한이면 요청 프로세스를 계속 진행
  • (5) 적절한 권한이 아닐 시 AccessDeniedException throw 후 ExceptionTranslationFilter가 해당 예외를 처리

0개의 댓글