티스토리 뷰

Spring/Security

SpringSecurity : 아키텍쳐

Bong Gu 2021. 10. 9. 17:30
728x90

스프링 시큐리티 : 아키텍쳐

SecurityContextHolder와 Authentication

SecurityContextHolder

  • SecurityContext 제공
  • 기본전략으로 ThreadLocal을 사용한다.
    • Global, Inheritable, ThereadLocal 등 전략을 선택할 수 있다.
    • ThreadLocal : 간단하게 말하면 한 Thread 내에서 Resource를 공유하는 저장소 (파라미터를 넘기지 않아도 객체에 접근이 가능)
    • Servlet기반의 애플리케이션(Servlet컨테이너를 사용하는)는 Async한 기능이 아니라면 요청당 하느의 Thread를 사용한다. (Thread per Request)

SecurityContext

  • Authentication 제공

Authentication

  • pricipal과 GrantAuthority 제공

Principal

  • "누구" 에 해당하는 정보
  • UserDetailsService에서 리턴한 객체
  • 객체는 UserDetails 타입

GrantAuthority

  • "Role_USER", "ROLE_ADMIN" 등 pricipal이 가지고 있는 권한을 말한다.
  • 인증 이후 인가 및 권한 확인 시, 해당정보를 참조한다.
  • "ROLE_"은 SimpleGrantAuthority등 Authority 생성 시 붙여준다. (입력시에는 "USER", "ADMIN" 과같이 저장)

UserDetails

  • 애플리케이션이 가지고 있는 유저정보와 스프링 시큐리티가 사용하는 Authentication객체 사이의 어댑터

UserDetailsService

  • 유저정보를 UserDetails타입으로 가져오는 DAO(Data Access Object) 인터페이스
  • 구현은 자유롭게

예제

security02-2security02-3

AuthenticationManager 와 Authentication

  • 스프링 시큐리티에서 인증은 AuthenticationManager가 한다.
  Authentication authenticate(Authentication authentication) throws AuthenticationException;
  • AuthenticationManager는 아래메소드 하나만을 가진다.
  • 인자로 받은 Authentication : 사용자가 입력한 인증 정보로 만든 객체 (폼 인증의 경우)
    • Principal : "Bong"
    • Credentials : "123"
  • 리턴 하는 Authentication : 인증된 객체를 Principal로 담은 객체
    • Principal : UserDetailsService에서 리턴한 객체 (UserDetails)
    • Credentials :
    • GrantAuthorities : 권한
  • AuthenticationException
    • BadCredentials (잘못된 인증정보)
    • Locked (잠김)
    • Disabled (비활성)
    • AuthenticationManager의 구현체
  • 대부분 ProviderManager 사용
  • ProviderManager
    • 여러 AuthenticationProvider에 위임한다.
    • 아무등록 안할 시, AnonymousAuthenticationProvider 하나를 가지고 있음
    • UsernamePasswordAuthenticationToken(인자로 받은 authentication)은 AnonymousAuthenticationProvider에서 처리 불가능
      • 부모 탐색 DaoAuthenticationProvider에서 처리가능
      • UserDetailsService에 구현한 loadUserByUsername 메소드에서 UserDetails를 가져온다.
      • UserDetails는 시큐리티가 Principal정보를 표현할 인터페이스
      • 가져온 UserDetials를 check 후에 만든 result 가 SecurityContextHolder.getContext().getAuthentication() 에 담기는 객체

ThreadLocal

  • Java.lang 패키지에서 제공하는 스레드 범위 변수
  • 스레드 수준의 데이터 저장소
  • 같은 스레드 내에서만 공유
  • 같은 스레드라면 해당 데이터를 메소드 매개변수로 넘겨줄 필요가 없다.
  • SecurityContextHolder의 기본 전략
package com.bong.demospringsecurityform.domain.Account;

public class AccountContext {

  private static final ThreadLocal<Account> ACCOUNT_THREAD_LOCAL
      = new ThreadLocal<>();

  public static void setAccount(Account account) {
    ACCOUNT_THREAD_LOCAL.set(account);
  }

  public static Account getAccount(Account account) {
    return ACCOUNT_THREAD_LOCAL.get();
  }

}

Authentication과 SecurityContextHolder

  • SecurityContextHolder 에서 꺼낸 Authentication
    • 모두 동일한 Authentication객체를 리턴하고 있다.
  • image-20210201230244466
  • UsernamePasswordAuthenticationFilter
    • 폼 인증을 처리하는 시큐리티 필터
    • authenticationManager.authenticate(authRequest) 를 통해 인증을 한다.
    • 인증된 Authentication 객체를 SecurityContextHolder에 넣어주는 필터
    • SecurityContextHolder.getContext().setAuthentication(authentication)
  • SecurityContextPersistenceFilter
    • SecurityContext를 HTTP session에 캐시(기본전략)하여 여러 요청에서 Authentication을 공유할 수 있도록 하는 필터
    • SecurityContextRepository를 교체하여 세션을 HTTP session이 아닌 다른 곳에 저장하는것도 가능하다.
    • 기본은 HttpSessionSecurityContextRepository
    • 가져오는것만 하는것이 아니라, session에 저장하는것도 한다.
    • 로그인 요청 -> UsernamePasswordAuthenticationFilter -> SecurityContextPersistenceFilter를 타서 세션에 저장
    • 매번 요청 시, 저장된 유저정보가 있는지 확인한다.

스프링 시큐리티 Filter와 FilterChainProxy

필터가 어디서 어떻게 사용되는지 알아보자.

  • FilterChainProxy를 통해 Filter목록들을 가져온다.
    • url pattern이 매치가 되면 매치가 된 chain의 filter들을 모두 가져온다.
  • 가져온 목록들을 순차적으로 실행한다.
    • 모든필터들은 FilterChainProxy가 호출한다.
    image-20210201233341516
  • 스프링 시큐리티가 제공하는 필터들
    • WebAsyncManagerIntergrationFilter
    • SecurityContextPersistenceFilter
    • HeaderWriterFilter
    • CsrfFilter
    • LogoutFilter
    • UsernamePasswordAuthenticationFilter
    • DefaultLoginPageGeneratingFilter
    • DefaultLogoutPageGeneratingFilter
    • BasicAuthenticationFilter
    • RequestCacheAwareFtiler
    • SecurityContextHolderAwareReqeustFilter
    • AnonymouseAuthenticationFilter
    • SessionManagementFilter
    • ExeptionTranslationFilter
    • FilterSecurityInterceptor
  • SecurityFIlterChain 만들어지고, 커스터마이징 되는곳은 SecurityConfig이다.
    • 조건에 따라 Filter 갯수가 다르다.
    • SecurityFilterChain을 여러개 설정할 수 있다. (여러가지 SecurityConfig를 만든다면..)

DelegatingFilterProxy와 FilterChainProxy

  • 일발적인 서블릿 필터
    • 어떤요청을 처리하는 앞뒤로 특정한 일을 할 수 있다.
  • 이름 그대로 처리를 스프링에 들어있는 빈으로 위임하고 싶을 때 사용하는 서블릿 필터.
    • 위임을 하기 위해서는 구체적인 명시가 필요한데, 타겟 빈 이름을 설정한다.
    • 스프링 부트를 사용할 때는 자동으로 등록이 된다.(SecurityFilterAutoConfiguration 에서..)
    • 스프링 부트 없이 시큐리티를 설정할 때는 AbstractSecurityWebApplicationInitializer를 사용해서 등록한다.

Delegate : 위임

  • 스프링 시큐리티 관점에서 보면 FilterChainProxy로 처리를 위임한다.
    • 보통 springSecurityFilterChain 이라는 이름의 빈으로 등록된다.
image-20210216083015035

AccessDecisionManager

  • 인가를 담당하는 매니저
  • Access Control (Authorization) 을 결정내리는 인터페이스로 3가지 구현체를 기본 제공한다.
    • AffirmativeBased : 여러 Voter중에 한명이라도 허용하면 허용 (기본전략)
    • ConsensusBased : 다수결
    • UnanimousBased : 만장일치
  • AccessDecisionVoter
    • 해당 Authentication이 특정한 Object에 접근할 때, 필요한 ConfigAttributes를 만족하는지 확인한다.
    • WebExpressionVoter : 웹 시큐리티에서 사용하는 기본 구현체
    • ROLE_Xxxx가 매치하는지 확인한다.
  • decide(...)
  • boolean supports(ConfigAttribute attribute)
    • 해당 ConfigAttribute 를 지원하는지 여부를 확인한다.
    • ConfigAttribute 의 예 hasRole(),permitAll() 등…

커스터마이징

  • AccessDecisionManager를 커스터마이징
    • 보다 간단하게 설정하기 위해서는 ExpressionHandler만 커스터마이징하는것도 가능하다.
    image-20210621090256270
  • image-20210621085943712

Filter Security Interceptor

  • AccessDecisionManager를 사용하여 Access Control 또는 예외 처리 하는 필터.
  • 대부분의 경우 FilterChainProxy에 제일 마지막 필터로 들어있다.
  • 접근이 허용되지않은 voter의 경우 (attribute를 확인하여), exception 을 뱉는다.
    • exception Handler등에 의해 이후 동작이 결정된다.
  • AbstractSecurityInterceptor : FilterSecurityInterceptor의 부모 클래스
    • accessDecisionManager를 사용하여 accessControl을 한다.
    • accessDecisionManager.decide(authenticated, object, attributes) 를 실행
    • 인가안된 사용자는 AccessDeniedException 발생하여 해당 에러를 처리하는 필터에서 처리한다.

ExceptionTranslationFilter

  • 필터 체인에서 발생하는 AccessDeniedException과 AuthenticationException을 처리하는 필터
  • AuthenticationException 발생 (인증예외)
    • AuthenticationEntryPoint 실행 (인증처리기, 인증이될때까지 인증을 시도, 로그인페이지로 리다이렉트)
    • AbstractSecurityInterceptor 하위 클래스(FilterSecurityInterceptor)에서 발생하는 예외만 처리한다.
    • UsernamePasswordAuthenticationFilter에서 발생한 인증은 ExceptionTranslationFilter에서 처리하지 않는다.
    • 해당 예외는 UsernamePasswordAuthenticationFilter안에서 직접 처리한다.
    • Session에 exception 메세지를 담아둔다.
    • LoginPageGenerating 하는 필터에서 뷰를 보여줄때 저장한 에러메세지와 같이 보여준다.
  • AccessDeniedException 발생 (인가예외)
    • 인증은 됬는데 인가 예외처리
    • 익명사용자라면 AuthenticationEntryPoint 실행
    • 익명사용자가 아닌 인증된 사용자라면 AccessDeniedHandler에게 위임

스프링 시큐리티 아키텍처 정리

image-20210920141302377
  • ServletContainer 안에 요청이 들어온다.
  • DeligatingFilterProxy가 서플릿 필터로 등록 (boot는 자동으로 등록)
  • FilterChainProxy에 위임한다. (springSecurityFilterChain이란 빈이름으로)
  • 여러 필터들을 체인형태로 가지고 있다.
    • WebSecurity라는것으로 만들어진다.(HttpSecurity도 같이 사용하여 만든다.)
    • WebSecurityConfigurerAdapter
      • WebSecurityConfiguration으로 만들어지면 이것으로 FilterChainProxy를 만드는것.
      • DelegatingFilterChainProxy가 위임하는 FilterChain
  • 이 필터들이 사용하는 주요한 객체들
    • AuthenticationManager 인증
    • 직접 구현하여 사용할 수 도 있지만, ProviderManager 구현체를 주로 사용한다.
    • ProviderManager는 여러 AuthenticationProvider를 사용해서 인증을 처리한다.
      • 그 중 하나가 DaoAuthenticationProvider 이고, DaoAuthenticationProvider 는 UserDetailsService라는 Dao 인터페이스를 사용해서 데이터에서 읽어온 유저정보를 사용하여 인증을 한다.
    • 인증을 성공하면, SecurityContextHolder에 넣어놓고 어플리케이션 전반에 걸쳐 사용한다.
      • SecurityContextHolder -> SecurityContext -> Authentication -> Principal, GrantAuthorities
      • 세션에 저장할 수 도 있다.
      • 이 정보는 SecurityContextPersistenceFilter에 의해 읽혀져 다시 사용할 수 도 있다.
    • AccessDecisionManager 인가
    • 인증 후에 FilterSecurityInterceptor에서 AccessDecisionManger를 사용해서 인가처리를 한다.(AccessControl)
    • 특정 url, 특정 메소드에 접근할 수 있는 적절한 config attribute 즉 적절한 ROLE을 가지고 있는가를 확인한다.
    • AffirmativeBased라는 전략을 기본으로 사용한다.
      • 여러 DecisionVoter중에 하나라도 허용을 한다면 그 요청을 허용한다.
      • 이 외에도 다수결, 만장일치 등의 전략이 있다.
    • AffirmativeBased가 사용하는 voter 중에 WebExpressionVoter 하나만을 사용한다.
    • WebExpressionVoter는 SecurityExpressionHanlder를 사용하여 Expression을 처리한다.
      • ROLE 계층형 권한을 위해 커스터마이징 할 수 있다.

728x90
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday