Posts Spring Security2
Post
Cancel

Spring Security2

Spring Security-2


DelegatingFilterProxy

image

Spring Security가 모든 App 요청을 감싸게 해서 보안이 적용되게 하는 서블릿 필터

  • 서블릿 필터는 스프링에서 정의된 빈을 주입해서 사용할 수 없음
  • 특정한 이름을 가진 스프링 빈을 찾아 그 빈에게 요청을 위임
    • springSecurityFilterChain 이름으로 생성된 빈을 ApplicationContext에서 찾아 요청을 위임
    • 실제 보안처리는 하지 않음
1
2
3
4
5
6
7
8
<filter>
    <filter-name>springSecurityFilterChain</filter-name>
    <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
    <filter-name>springSecurityFilterChain</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

이런식으로 필터를 적용하여 모든 요청을 Spring Security가 감싸서 처리할 수 있다.

  • DelegatingFilterProxy는 filtername으로 명시된 Spring-Context에서 빈으로 등록된 SpringFilterChain에게 실제 인증처리를 위임하기만 함
  • springSecurityFilerChain은 Filter 인터페이스를 구현한 여러종류의 Filter들을 Filer-List 객체에 가지고 있음
필터하는 일
SecurityContextPersistenceFilterSecurityContextRepository에서 SecurityContext를 로드하고 저장하는
일을 담당함
LogoutFilter로그아웃 URL로 지정된 가상URL에 대한 요청을 감시하고 매칭되는
요청이 있으면 사용자를 로그아웃시킴
UsernamePasswordAuthenticationFilter사용자명과 비밀번호로 이뤄진 폼기반 인증에 사용하는 가상 URL
요청을 감시하고 요청이 있으면 사용자의 인증을 진행함
DefaultLoginPageGeneratingFilter폼기반 또는 OpenID 기반 인증에 사용하는 가상URL에 대한 요청을
감시하고 로그인 폼 기능을 수행하는데 필요한 HTML을 생성함
BasicAuthenticationFilterHTTP 기본 인증 헤더를 감시하고 이를 처리함
RequestCacheAwareFilter로그인 성공 이후 인증 요청에 의해 가로채어진 사용자의 원래
요청을 재구성하는데 사용됨 HttpServletRequest를
HttpServletRequestWrapper를 상속하는 하위 클래스
(SecurityContextHolderAwareRequestWrapper)로 감싸서 필터
체인상 하단에 위치한 요청 프로세서에 추가 컨텍스트를 제공함
AnonymousAuthenticationFilter이 필터가 호출되는 시점까지 사용자가 아직 인증을 받지 못했다면
요청 관련 인증 토큰에서 사용자가 익명 사용자로 나타나게 됨
SessionManagementFilter인증된 주체를 바탕으로 세션 트래킹을 처리해 단일 주체와 관련한
모든 세션들이 트래킹되도록 도움
ExceptionTranslationFilter이 필터는 보호된 요청을 처리하는 동안 발생할 수 있는 기대한
예외의 기본 라우팅과 위임을 처리함
FilterSecurityInterceptor이 필터는 권한부여와 관련한 결정을 AccessDecisionManager에게
위임해 권한부여 결정 및 접근 제어 결정을 쉽게 만들어 줌

Filter 초기화 와 다중 설정 클래스

image

  • 각각의 Config 파일 별로 다른 Security Filter가 생성되고 이 Filer는 antMatch 메서드(Request Matcher 설정)로 각각 별도의 URL에 매핑됨
  • 모든 필터들은 SecurityFilters라는 리스트에 담기게 되고 이는 FilterChainProxy가 가지고 있음
    • 즉 URL을 FilterChainProxy가 받아서 리스트에서 어떤 SecurityFilter를 사용할지를 결정

Config 클래스 등록

  • AbstractConfiguredSecurityBuilder
    • configure(): 각 config class를 불러와서 config 작업을 수행
      • SecurityConfigurer(I)로 저장(수많은 Configurer들이 상속)

즉 webSecurityConfigurerAdapter가 annotation에 의해 Config로 등록되는 과정 속에서 http(AbstractConfiguredSecurityBuilder)가 config와 customConfigurerAjax에 의해 초기화 되면서 config 등록

Filter Process

  1. WebSecurity Class: FilterChainProxy를 Bean으로 등록(new FilterChainProxy(securityChains))
    • securityChains 안에 필터와 requestMatcher가 있음
  2. 초기화가 끝나고 Request가 들어오면 FilterProxy에서 사용할 Filter를 고름(getFilters())

Authentication

누구인지를 증명하는 것

  • 사용자의 인증 정보를 저장하는 토큰 개념
  • 인증 시 id 와 password를 담고 인증 검증을 위해 전달되어 사용
  • 인증 후 최종 인증 결과(user 객체, 권한 정보)를 담고 Security Context에 저장되어 전역적으로 참조가 가능
    • Authentication authentication = SecurityContextHolder.getContext().getAuthentication()
  • 구조
    • principal: 사용자 id 혹은 User 객체 저장
    • credential: 사용자 비밀번호
    • authorities: 인증된 사용자의 권한 목록
    • details: 인증 부가 정보
    • Authenticated: 인증 여부

Authentication Process

image

  1. login 정보(id, password)가 전달 됨
  2. Filter가 Request를 가로채어 Authentication이라는 객체를 생성
    • FormLogIn이면 UsernamePasswordAuthenticationToken이 생성됨
    • Manager에 전달
  3. AuthenticationManager(I)가 ProviderManager(C)를 통해 AuthenticationProvider 목록 중에서 인증 처리 요건에 맞는 AuthenticationProvider를 찾아 인증처리를 위임
    • 부모 ProviderManager를 설정하여 AuthenticationProvider를 계속 탐색 할 수 있음.
    • OAuth일 경우 parent 속성을 통해 부모격의 Provider에서 OAuth 인증 Provider를 사용
      • 초기화 과정에서 2개의 ProviderManager를 만드는데 하나가 부모용 하나가 Default
      • Provider 찾다가 Default에 없으면 부모로 가서 찾음
      • Token의 타입을 통해 Provider를 찾음
    • AuthenticationManagerBuilder를 통해 생성됨(Provider가 생성됨)
  4. AuthenticationProvider(I)를 상속한 무슨무슨AuthenticationProvider(C)가 인증처리를 수행
    • provider.authenticate(authentication)
      • ID 검증: UserDetailsService에서 수행(UserDetails 반환)
      • password 검증: 실패 시 BadCredentialException
      • 추가 검증
    • supports: 인증을 처리할 수 있는 Provider인지 확인
  5. UserDetailsService에서 실제 인증 처리 역할
    • 유저 유효성 체크
  6. Repository에서 유저 객체 조회, UserDetails 타입으로 반환
  7. UserDetailsService의 UserDetails로 Provider가 Authentication 객체를 만듬(권한 목록이 채워지는등의 정보가 달라짐)
    • 최종적으로 필터에게 전달
  8. SecurityContextHolder의 SecurityContext의 Authentication 객체에 저장됨(필터에서 처리함)

SecurityContext, Security Context Holder

SecurityContext: Authentication 객체가 저장되는 Context

  • ThreadLocal에 저장되어 아무곳에서나 참조가 가능하도록 설계
    • ThreadLocal: 각 Thread에 할당되는 메모리(공유 불가)
  • 인증이 완료되면 HttpSession에 저장되어 App 전반에 걸쳐 전역적인 참조가 가능

  • Security 객체 저장 방식(SecurityContextHolder)
    • MODE_THREADLOCAL: thread당 SecurityContext 객체를 할당, 기본값
    • MODE_INHERITABLETHREADLOCAL: 메인 Thread 와 자식 Thread에 관하여 동일한 SecurityContext 유지
    • MODE_GLOBAL: 응용 프로그램에서 단 하나의 SecurityContext를 저장
  • SecurityContextHolder.cleanContext(): SecurityContext 기존 정보 초기화

Security Process

  1. Login 요청이 들어오면 Server가 Thread를 생성
    • ThreadLocal이라는 저장소 생성
  2. 인증처리(Filter): Authentication 객체 생성
  3. 인증 성공 시 SecurityContext에 Authentication 객체 저장
  4. HttpSession에 저장
    • Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    • SecurityContext sc = (SecurityContext)session.getAttribute(HttpSessionSecurityContextRepository.SPRING_SECURITY_CONTEXT_KEY);
    • Authentication authentication = context.getAuthentication();
    • 2가지 방법으로 authentication 객체 획득 가능

SecurityContextPersistenceFilter

SecurityContext 객체의 생성, 저장, 조회

  • 익명 사용자
    • 새로운 SecurityContext를 생성하여 SecurityContextHolder에 저장(SecurityContextPersistenceFilter가 수행)
    • AnonymousAuthenticationFilter에서 AnonymousAuthentificationToken를 SecurityContext에 저장
  • 인증 시
    • 새로운 SecurityContext를 생성하여 SecurityContextHolder에 저장(SecurityContextPersistenceFilter가 수행)
    • UsernamePasswordAuthentificationFilter에서 인증 성공 후 해당 토큰 SecurityContext에 저장
    • 인증이 최종 완료되면 Session에 SecurityContext 저장
  • 인증 후
    • Session에서 SecurityContext 꺼내서 SecurityContextHolder에 저장(SecurityContextPersistenceFilter가 수행)
    • SecurityContext 안에 Authentication 객체가 존재하면 계속 인증을 유지
  • 최종 응답 시 공통
    • SecurityContextHolder.clearContext();

SecurityContextPersistenceFilter Process

  1. FilterChain안의 SCPF가 요청을 받음
  2. HttpSecurityContextRepository가 요청을 전달 받음
  3. 인증 상태 확인
  4. 새로운 SecurityContext 생성
  5. chain.doFilter를 통해 인증 필터(AuthFilter)를 통과하게 됨(인증 작업)
  6. 인증 후 SecurityContext에 Authentication 객체를 저장
  7. Session에 SecurityContext 저장
  8. SecurityContext를 holder에서 삭제
  9. 응답

Authorization

무엇이 허가 되었는가

  • Spring Security가 지원하는 권한 계층
    • 웹계층: URL 요청에 따른 메뉴 혹은 화면 단위의 레벨 보안
    • 서비스 계층: 화면 단위가 아닌 메서드 같은 단위의 레벨 보안
    • 도메인 계층: 객체 단위의 레벨 보안
  • FilterSecurityInterceptor
    • 마지막에 위치한 필터로서 인증된 사용자에 대하여 특정 요청의 승인/거부 여부를 최종적으로 결정
    • 인증객체 없이 보호자원에 접근을 시도할 경우 AuthenticationException을 발생
    • 인증 후 자원에 접근 가능한 권한이 존재하지 않을 경우 AccessDeniedException을 발생
    • 권한 제어 방식 중 HTTP 자원의 보안을 처리하는 필터
    • 권한 처리를 AccessDesicionManager에 위임

Authorization Process

  1. 요청이 들어오면 FilterSecurityInterceptor가 인증 여부를 체크
  2. SecurityMetaSource(C): 사용자가 요청한 자원에 필요한 권한 정보를 조회해서 전달
  3. 권한 정보가 존재할 시 AccessDecisionManager(C)로 전달
    • AccessDecisionVoter를 통해 심의 수행
    • 최종 승인 or 거부

AccessDecisionManager

  • 인증정보, 요청정보, 권한정보를 통해 사용자의 자원접근을 허용할 것인지 거부할 것인지 최종 결정하는 주체
  • 여러개의 Voter들로 부터 접근허용, 거부, 보류에 해당하는 각각의 값을 리턴받고 판단 및 결정
  • 최종 접근 거부 시 예외 발생

접근 결정의 세가지 유형

  • AffirmativeBased
    • 여러개의 Voter 클래스 중 하나라도 접근 허가로 결론을 내면 허가로 판단한다.
  • ConsensusBased
    • 다수표(승인 및 거부)에 의해 최종 결정을 판단한다
    • 동수일 경우 기본은 접근허기이나 allowIfEqualGrantedDeniedDecisions을 false로 설정할 경우 접근 거부로 결정됨
  • UnanimouseBased
    • 모든 Voter가 만장일치로 접근을 승인해야 하며 그렇지 않을 경우 접근을 거부

AccessDecisionVoter

  • Voter가 권한 부여 과정에서 판단하는 자료
    • Authentication: 인증 정보(user)
    • FilterInvocation: 요청 정보(antMatcher(“/user”))
    • ConfigAttributes: 권한 정보(hasRole(“USER”))
  • 결정 방식
    • ACCESS_GRANTED: 접근 허용(1)
    • ACCESS_DENIED: 접근 거부(0)
    • ACCESS_ABSTAIN: 접근 보류(-1)
      • Voter가 해당 타입의 요청에 대해 결정을 내릴 수 없는 경우

AccessDecisionManager Process(인가 처리)

  1. FilterSecurityInterceptor(C)가 AccessDecisionManager(I)에 인가 처리 위임
    • decide(authentication, object, configAttributes)
    • 순서대로 Authentication(인증정보), FilterInvocation(요청정보), ConfigAttributes(권한정보)
  2. AccessDecisionManager(I)가 AccessDevisionvoter(I)를 통해 위의 3가지 정보를 이용하여 접근 결정
    • AccessDecisionManager(I) 구현체: AffirmativeBased, ConsensusBased, UnanimousBased
    • decide()를 통해 결정
    • voter(I)의 구현체: RoleVoter: supports()를 통해 return

인가 처리

  • FilterSecurityInterceptor로 요청이 들어옴
    • FilterInvocation 생성 및 invoke(FilterInvocation) 실행
    • invoke 실행 중 부모 클래스(AbstractSecurityInterceptor)의 beforeInvocation() 실행(권한 목록 가져옴)
      • obtainSecurityMetadataSource() 실행 -> ExpressionBasedFilterInvocationSecurityMetadataSource를 가지고 있으면 이것의 getAttributes를 통해 권한목록 가져옴
        • 일치하는 것이 없으면 null을 return 하는데 이러면 따로 인증이나 인가 처리를 하지 않음
        • /admin/** 이 등록되어 있으면 제한된 사용자만 접속 허용 등록 안되 있는것들은 그냥 허용(return null)하겠다는 뜻
      • AuthenticateIfRequired(): 인증 정보
      • attemptAuthorization() 실행: accessDecisionManager로 정보를 보내서 인가 여부 확인

메서드 방식 인가처리

  1. DefaultAdvisorAutoProxtCreator가 MethodSecurityMetadataSourceAdvisor를 통해 Bean을 검사한 후 해당 Bean의 Proxy 객체를 생성(초기화).
    1. MethodSecurityMetadataSourcePointcut이 MSMSA의 MethodSecurityMetadataSource를 받아서 getAttrubutes를 통해 Bean 과 권한정보을 찾습니다.
      • 찾은 Bean들의 정보를 이용해 Interceptor에 advisor(MethodSecurityInterceptor)를 등록합니다.
      • 권한 정보는 attributeCache에다가 저장
      • 즉 annotation이 달린 메서드는 Proxy 생성 대상
  2. 생성된 Proxy 객체 안에 MSMSA도 담김 (MSMSA 안에 PointCut이랑 Interceptor 다있음)
  3. 요청이 들어오면 해당 객체의 Proxy로 넘어가서 MethodSecurityInterceptor로 요청을 넘기면서 인가가 시작됨
    • DelegatingMethodSecurityMetadataSource가 가진 Prepost…나 Secured… 를 통해(getAttributes할때 캐쉬에 담긴 Attributes를 가져옴) 인가 처리
    • custom하게 metadatasource 추가 가능
  • PrePostAnnotationSecurityMetadataSource: extracts metadata from the @PreFilter and @PreAuthorize annotations placed on a method.
    • 이것의 getAttributes는 attr을 return 하는데 여기에 표현식 등이 담김
  • SecuredAnnotationSecurityMetadataSource: 똑같음 얘는 extractAttributes
This post is licensed under CC BY 4.0 by the author.