본문 바로가기
관리자

Programming-[Backend]/Spring Security

[스프링 시큐리티][작성중][메모] 9. 주요 아키텍처 이해

728x90
반응형

1. 위임 필터 및 필터 빈 초기화

서블릿 컨테이너가 사용하는 filter 중 DelegatingFilterProxy라는 필터가 있어서, 스프링 컨테이너에서 이를 활용한다. DelegatingProxyChainFilter는 springSecurityFilterChain이라는 이름의 스프링 빈을 찾아서 서블릿 컨테이너에 등록하고 사용할 수 있도록 한다. springSecurityFilterChain이 앞선 글들에서 살펴봤던 FilerChainProxy이다.

 

SecurityFilterAutoConfiguration이 서블릿 컨테이너에서 필터들을 등록하는 클래스이다. 스프링의 컨테이너는 WebSecurityConfiguration이며 여기서 생성하는 Bean이 FilterChainProxy이다. FilterChainProxy를 순서대로 타는 것은 앞서 직접 알아봤던 내용과 같다.

 

2. 필터 초기화와 다중 보안 설정

여러 url에 따라서 SecurityConfig를 여러 개로 쪼개서 설정할 수 있다. 예를 들어 http.antMatcher("/admin/**")이라는 url은 SecurityConfig1쪽을 타도록 하고, SecurityConfig2 쪽은 타지 않도록 할 수 있다. 또는 두 개의 설정 클래스에 모두 작동하도록 할 수도 있다.

 

SecurtiyFilterChain이라는 객체에 각 설정에서 지정해준 Filter와 RequestMatcher가 생성된다. 그리고 이 SecurityFilterChain이라는 객체의 리스트가 FilterChainProxy의 SecurityFilterChains에 담기게 된다. 그림을 그려서 표현하는 게 좋을 것 같다.

 

FilterChainProxy의 matchers 메서드가 각 설정의 RequestMatcher에 설정된 url과 요청의 url에 대해 일치여부를 판단하고 filter들을 거치도록 설정해준다.

 

-> formLogin-anyRequest, httpBasic-"/admin/**" url 로 설정된 2개의 설정 파일을 만들고 작동방식을 살펴본다. 이해하는데 중요하므로 정리가 필요할 것 같다.@Order로 빈의 order를 설정해줄 수 있다. @Order의 순서에 따라서 Spring security가 먼저 참고하는 설정 정보가 달라지게 된다. 참고 자료를 더 찾아보자.

 

3. Authentication

 

id, password를 통해 인증하고, 인증 결과인 user 객체와 권한정보를 SecurityContext에 저장하며, 이 내용은 전역적으로 참조가 가능하다.

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

 

UsernamePasswordAuthenticationFilter -> Authentication 객체 -> AuthenticationManager에서 인증 처리 -> 성공 시 Authentication 성공 객체(credentials는 암호화됨) 저장 -> ThreadLocal(SecurityContextHolder)에 Authentication을 등록하여 전역적으로 사용

 

ThreadLocal은 각 쓰레드마다 사용되는 저장소이다. 쓰레드 전역에서 공유가 가능하지만, 다른 쓰레드에서는 참조할 수 없다.

 

UsernamePasswordAuthenticationToken 객체는 principal, credentials만 담는 생성자가 있고, principal, credentials 및 authorities를 담는 생성자가 있다. 최초 인증 시에는 첫 번째 생성자가 생성되고, 해당 사용자의 권한 정보를 찾아와서 두 번째 생성자를 통해 Authentication 객체를 생성한다. 이것이 AbstractAuthenticationProcessingFilter이며 여기서 authResult를 SecurityContextHolder에 저장한다. (SecurityContextHolder.getContext().setAuthentication(authResult))

 

 

4. SecurityContextHolder, SecuritContext

 

SecurityContext

Authentication 객체가 저장되는 보관소로, 여기서 Authentication 객체를 언제든지 꺼내어 쓸 수 있다.

 

SecurityContextHolder

SecurityContext 객체를 저장하는 방식을 지정할 수 있다. 부모 스레드(메인 스레드)와 자식 스레드가 있는 경우 SecurityContext를 어떤 형태로 저장할 지를 설정한다. 설정은 설정 클래스의 configure 메서드에서 SecurityContextHolder.setStrategyName() 메서드를 이용하여 할 수 있다.

 

  • MODE_THREADLOCAL : 스레드마다 SecurityContext를 할당한다. 기본값이다.
  • MODE_INHERITABLETHREADLOCAL : 메인 스레드와 자식 스레드에 대해 동일한 SecurityContext를 유지한다.
  • MODE_GLOBAL : 응용 프로그램에서 단 하나의 SecurityContext를 저장한다. static 변수로 SecurityContext를 저장하는 방식이다.

HttpSession에 SecurityContext 객체를 저장하므로, HttpSession.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY) 구문을 통해 Authentication 객체를 가져올 수도 있다.

 

SecurityContextHolder 클래스 살펴보기, 스레드별 SecurityContext 참조 가능 여부 확인

 

 

5. SecurityContextPersistenceFilter

 

SecurityContext를 생성, 저장, 조회하는 필터이다. 익명사용자, 인증 시, 인증 후, 공통 사항이 글로 되어 있는데, 그림으로 정리하면 더 좋을 것 같다.

 

여러 필터들이 Authentication 객체를 쓸 수 있도록 하기 위해서 SecurityContextPersistenceFilter는 2번째에 위치한다. 여기서 SecurityContext를 이용해서 Authenticaiton 객체를 꺼내고 나서 다음 필터에 계속해서 전달해주는 형태로 작동한다.

 

SecurityContextPersistenceFilter에서 인증 전인지, 인증 후인지를 판단하여 SecurityContext에 Authentication 객체를 넣어줄지 말지를 결정한다. Session에서 SecurityContext를 가져오는 과정을 통해 인증 전이나 후를 판단하고 다음 과정들을 진행하게 된다.

 

6. Authentication Flow

 

AuthenticationManager : 인증의 전반적인 관리를 하지만, 직접 인증처리를 하지는 않고 인증처리는 AuthenticationProvider에게 맡긴다. Manager 내의 Provider들을 담고 있는 List<Provider> 중 현재 요청 정보를 통해 인증 처리를 할 수 있는 Provider에게 인증 처리를 위임하게 된다.

 

AuthenticationProvider : 실제 검증 및 인증 처리를 한다. UserDetailsService 객체에게 user의 정보를 조회 요청한다. 이를 담당하는 메서드는 loadUserByUsername 메서드이다.

 

UserDetailsService : Repository에 접근하여 DB상에 유저 정보가 존재하는지 확인한다. 확인 결과를 UserDetails 타입으로 반환한다. password가 일치하지 않는 경우 Bad Credential Exception을 날린다.

 

 

7. AuthenticationManager

 

Form인증, rememberMe 인증, OAuth 인증 등의 여부에 따라서 ProviderManager가 인증 객체를 받고, 여러 ProviderManager 중 인증 방식과 일치하는 Provider가 인증을 처리한 뒤, AuthenticationManager에게 인증 결과를 전달한다.

 

1차적인 ProviderManger에서 사용자가 요청한 방식으로 인증 처리를 할 수 없다면, 부모 타입의 ProviderManager를 탐색하여 사용자가 요청한 인증 방식을 처리하는 Provider를 찾는다. 만약 처리할 수 있는 Provider가 없다면 예외를 발생시킨다.

 

 

8. AuthenticationProvider

크게 2개 메서드로 나뉜다. 현재 Provider에서 요청을 처리할 수 있는지를 판단하는 supports, 인증을 하는 authenticate 메서드가 있다. ID에 해당하는 user가 없는 경우 UserNotFoundException, password가 일치하지 않는 경우 BadCredentialException일 발생시킨다.

 

 

9. Authorization, FilterSecurityInterceptor

 

권한 계층

스프링 시큐리티가 지원하는 권한 계층은 다음과 같다.

  • 웹 계층 : 일반적으로 생각하는 URL 요청에 따른 단위이다. 메뉴 혹은 화면 단위이다.
  • 서비스 계층 : 메서드와 같은 기능 단위의 보안이다. 같은 화면에서 여러 기능이 있는 경우, 특정 기능만 어떤 권한에 종속되도록 할 수 있다.
  • 도메인 계층 : 객체 단위의 보안으로 특정 파일이나 DB 등에 대한 보안 계층을 의미한다.

 

FilterSecurityInterceptor

FilterChain의 맨 마지막에 위치한 필터이다. 인증 및 권한 정보를 검색하여 사용자의 요청에 대한 최종 승인 여부를 결정한다. 실제 권한에 따른 처리는 AccessDecisionManager에게 맡기는데, 이는 다음 장에서 배운다.

 

FilterSecurityInterceptor는 우선 인증 여부를 체크하고, 인증 객체가 존재하지 않는다면 AuthenticationException을 던진다. 인증 객체가 있는 경우 권한 검사를 시작하는데, 이 정보는 SecurityMetadataSource에서 확인한다. 이에 관해서도 다음 장에서 배운다.

 

MetadataSource 부분이 .antMatchers()로 설정한 url과 role에 대한 정보이다. 그리고 이 정보를 FilterSecurityInterceptor에서 이용한다.

 

AccessDecisionManager

SecurityMetadataSource를 거친 후, 여기서 권한 정보가 필요한지 확인한다. 권한 정보가 필요하지 않는 경우 자원 접근을 바로 허용하지만, 권한 정보가 필요한 경우 AccessDecisionManager에서 요청에 대한 심의를 진행한다. 심의 진행 객체는 AccessDecisionVoter 객체이며 결과를 반환받아서 AccessDecisionManager가 최종 접근 가능 여부를 결정을 하게 된다. (그림이 좋을 거 같음)

 

Voter들에 의한 접근 결정은 아래 세 가지 유형으로 나뉜다.

 

  • AffirmativeBase : 여러 Voter 클래스 중 하나라도 접근 허가가 되면 접근 허가
  • ConsensusBased : 다수에 의해 결정한다. 동수일 경우 기본은 접근 허가이며, allowIfEqualGrantedDeniedDecisions를 false로 변경하면 접근 거부가 된다.
  • UnanimousBased : 만장일치가 되야만 접근 허가된다.

 

Voter의 판단자료 : AccessDecisionManager가 AccessDecisionVoter에게 전달해주는 인자값이다.

  • Authentication : 인증 정보(user)
  • FilterInvocation : 요청정보(antMatcher("/user"))
  • ConfigAttributes : 권한 정보(hasRole("USER"))

 

결정방식 : AccessDecisionVoter가 AccessDecisionManager에게 반환하는 값

  • ACCESS_GRANTED : 접근허용(1)
  • ACCESS_DENIED : 접근 거부(-1)
  • ACCESS_ABSTAIN : 접근 보류(0)

 

 

 


 

참조

 

1) 인프런 - 스프링 시큐리티 - Spring Boot 기반으로 개발하는 Spring Security - 정수원님 강의

https://www.inflearn.com/course/%EC%BD%94%EC%96%B4-%EC%8A%A4%ED%94%84%EB%A7%81-%EC%8B%9C%ED%81%90%EB%A6%AC%ED%8B%B0

728x90
반응형