• DelegatingFilterProxy

    Untitled

    • 서블릿 필터는 스프링에서 정의된 빈을 주입해서 사용할 수 없음 → 서블릿 컨테이너와 스프링 컨테이너는 서로 접근 불가능
    • 특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 위임하는 역할
      • springSecurityFilterChain 이름으로 생성된 빈(FilterChainProxy 타입)을 ApplicationContext에서 찾아 요청을 위임
      • 실제 보안처리를 수행하지 않음(보안처리는 각 요청에 따른 Filter에서 수행)
    • 즉 예시는 Servlet Filter에서 요청을 받고 DelegatingFilterProxy로 포워딩되고 여기서 스프링 빈으로 등록된 Filter를 요청에 맞게 적용
    • 스프링 빈을 Servlet Filter에서 바로 사용할 수 없기 때문에 이런 방식을 이용(Servlet, Spring Container가 다르기 때문)
  • FilterChainProxy

    Untitled

    • SpringSecurityFilterChain 이름으로 생성되는 필터 빈
    • DelegatingFilterProxy로부터 요청을 위임 받고 필터들을 통해 실제 보안 처리
    • 스프링 시큐리티 초기화 시 생성되는 필터들을 관리/제어
      • 스프링 시큐리티가 기본 제공하는 필터
      • SecurityConfig에서 API로 설정으로 생성한 필터
    • 전체적으로는 사용자의 요청을 필터 순서대로 호출하여 전달 → 정확히는 순서대로 실행하면서 조건에 맞지 않는 경우 다음 필터 작동
    • 사용자는 CustomFilter를 정의하여 기존의 필터 전/후로 설정 가능 → 순서에 유의
    • 마지막 필터까지 예외가 발생하지 않는다면 보안을 통과
  • DelegatingFilterProxy, FilterChainProxy의 전체적인 흐름

    Untitled

    • 기본적으로 Servlet/Spring Container로 영역이 구분되어 Servlet 요청에서 스프링 빈을 활용하지 못하기 때문에 이런 방식을 활용
    • DelegatingFilterProxy 앞/뒤 필터는 Servlet Container Filter
    • 요청의 보안이 이루어지는 과정
      • 요청 발생
      • ServletContainer의 DelegatingFilterProxy가 요청 확인
      • DelegatingFilterProxy에서 스프링 빈으로 생성된 Filter를 활용할 수 없으므로 스프링 빈을 활용할 수 있는 FilterChainProxy로 요청 위임
      • FilterChainProxy는 springSecurityFilterChain이라는 이름의 빈으로 SpringSecurityFilter 빈들을 관리
      • 순서에 맞게 필터링 처리를 수행하고 마지막까지 인증/인가 예외가 발생하지 않는다면 정상적인 보안처리 완료 및 DispatcherServlet으로 이동하여 요청에 맞는 스프링 빈에게 위임
  • 필터 초기화와 다중 보안 설정

    SecurityConfig를 여러개 작성하는 경우

    SecurityConfig를 여러개 작성하는 경우

    SecurityConfig를 여러개 작성하는 경우 요청 진행 과정

    SecurityConfig를 여러개 작성하는 경우 요청 진행 과정

    • 설정 클래스 별로 보안 기능 및 RequestMatcher 설정
    • 설정 클래스 별로 필터가 생성
    • FilterChainProxy가 각 설정 클래스들의 필터들을 클래스별로 관리 → FilterChainProxy의 SecurityFilterChains에서 관리
    • 요청에 따라 RequestMatcher와 매칭되는 필터가 작동되도록 함
    • 하지만 같은 클래스를 상속한 빈이므로 unique 에러가 발생하고 실행할 수 없음 → @Order 어노테이션을 사용해서 빈 중복 해결
    • @Order 어노테이션을 사용할 때 주의해야 하는 점은 어떤 클래스를 우선적으로 빈으로 등록하냐에 따라 원하는 path에 대해 보안처리가 원하는 대로 안될 수도 있음(주의)
  • Authentication(인증, 인증 객체)

    • 사용자가 누구인지 증명하는 것

    • 사용자의 인증 정보를 저장하는 토큰 개념 → 인증 시도할 때 username, password를 담고 인증 검증을 위해 전달되어 사용되며 인증 후에는 인증 객체를 담기 위해 사용함

    • 인증 후 최종 인증 결과(사용자 객체, 비밀번호, 권한 목록)를 담고 SecurityContext에 저장되어 전역에서 참조 가능

      // SpringSecurity 인증 객체 확인 코드
      Authentication auth = SecurityContextHolder
      	.getContext().getAuthentication();
      
    • 인증 객체 구조(Authentication 객체)

      • principal = 사용자 아이디 혹은 객체를 저장
      • credentials = 사용자 비밀번호
      • authorities = 인증된 사용자의 권한 목록
      • details = 인증 부가 정보
      • Authenticated = 인증 여부
    • 인증 과정

      AuthenticationManager가 인증을 처리하고 예외도 발생

      AuthenticationManager가 인증을 처리하고 예외도 발생

  • SecurityContextHolder, SecurityContext

    Untitled

    • SecurityContext

      • Authentication 객체가 저장되는 보관소로 필요 시 언제든지 Authentication 객체를 꺼내 쓸 수 있어야 함

      • ThreadLocal에 저장되어 같은 Thread에서 참조 가능하도록 함(ThreadLocal은 각 Thread의 저장소)

        @GetMapping("")
        public String index() {
        	// 아래는 새로운 Thread를 만들어 진행하는 코드(자식 Thread 생성 -> 모드 변경 시 접근 가능)
        	// 즉, Thread가 다르므로 서로 다른 ThreadLocal 참조
        	// 다른 ThreadLocal이므로 Authentication 객체 접근 불가능
        	new Thread(() -> {
        		Authentication auth = SecurityContextHolder.getContext().getAuthentication();
        	}).start();
        	return "index";
        }
        
      • 인증이 완료되면 HttpSession에 저장되어 전역에서 참조 가능

        @GetMapping("")
        public String index(HttpSession session) {
        	SecurityContext ctx = (SecurityContext) session
        		.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
        	Authentication auth = ctx.getAuthentication();
        	return "index";
        }
        
    • SecurityContextHolder

      • SecurityContext 객체 저장 방식

        • MODE_THREADLOCAL = 스레드별로 SecurityContext 객체 할당(기본값)

        • MODE_INHERITABLETHREADLOCAL = 메인 스레드와 자식 스레드에 관하여 동일한 SecurityContext 유지

        • MODE_GLOBAL = 응용 프로그램에서 단 하나의 SecurityContext 저장

        • 객체 저장 방식의 설정은 SecurityConfig에서 수행 가능

          Untitled

      • SecurityContextHolder.clearContext() 메서드를 이용해서 SecurityContext 정보 초기화

  • SecurityContextPersistenceFilter

    Untitled

    요약하면 인증을 하기 전에는 새로운 SecurityContext 객체를 생성하고
인증이 완료된 경우에는 session에서 SecurityContext 객체를 가져와서 인증을 유지하는 필터

    요약하면 인증을 하기 전에는 새로운 SecurityContext 객체를 생성하고 인증이 완료된 경우에는 session에서 SecurityContext 객체를 가져와서 인증을 유지하는 필터

    • SecurityContext 객체의 생성, 저장, 조회 역할
      • 익명 사용자
        • 새로운 SecurityContext 객체를 생성하여 SecurityContextHolder에 저장
        • AnonymousAuthenticationFilter에서 AnonymousAuthenticationToken 객체를 SecurityContext에 저장
      • 인증 사용자
        • 새로운 SecurityContext 객체를 생성하여 SecurityContextHolder에 저장
        • UsernamePasswordAuthenticationFilter에서 인증을 성공하면 SecurityContext에 UsernamePasswordAuthenticationToken 객체를 SecurityContext에 저장
        • 인증이 완료되면 Session에 SecurityContext 저장
      • 인증 이후
        • Session에서 SecurityContext 꺼내어 SecurityContextHolder에 저장
        • SecurityContext 안에 Authentication 객체가 존재하면 인증 유지
      • 즉, 어떤 경우에도 SecurityContext 객체의 생성 주기 관리
  • Authentication Flow

    Untitled

  • AuthenticationManager

    Untitled

    • AuthenticationProvider 목록 중에서 인증 처리 요건에 맞는 AuthenticationProvider를 찾아 인증처리를 위임(AuthenticationProvider가 실제 인증을 처리)
    • 부모 ProviderManager를 설정하여 AuthenticationProvider를 계속 탐색할 수 있다.(OauthAuthenticationProvider의 경우) → 기존 ProviderManager에 인증할 수 있는 Provider가 존재하지 않는 경우 탐색

    Untitled

    • 이처럼 Linked 형태로 부모와 자식 관계를 형성할 수 있음
    • 자식에서 적절한 AuthenticationProvider를 찾지 못한 경우 부모로 올라가면서 Provider를 탐색
    • AuthenticationManagerBuilder를 사용해서 스프링 시큐리티가 설정한 기본 부모관계 설정을 변경해야 모든 ProviderManager의 Provider 탐색이 가능
  • AuthenticationProvider

  • Authorization(인가)

  • AccessDecisionManger, AccessDecisionVoter

  • 스프링 시큐리티 주요 아키텍처 이해