들어가며...
저는 "프레임워크의 설계를 이해하고 있느냐 아니냐" 에 따라, 개발을 진행하면서 얻는 경험의 깊이가 다르다고 생각합니다. 프레임워크의 설계를 이해하고 있으면 프레임워크를 설계한 의도에 맞게 사용하고자 노력하게 되며, 이는 곧 좋은 코드를 만들려는 습관으로 이어지게 됩니다.
개발자는 레퍼런스 문서를 전부 읽고 개발을 시작하지 않습니다. 검색을 통해 여러 샘플 코드를 적용해보면서 프레임워크를 어떻게 사용해야 하는지 경험하게 됩니다. 이때 얻는 경험이 단편적인 지식으로 그치는게 아니라, 축적되는 지혜가 되기 위해서는 프레임워크 설계에 대한 이해가 필요하다 생각합니다.
Spring Security (Servlet Application)
Spring Security Overview는 Spring Security 를 아래와 같이 설명합니다.
Spring Security is a framework that focuses on providing both authentication and authorization to Java applications.
Spring Security 는 자바 어플리케이션에게 인증과 허가를 제공하는데 집중한 프레임워크다.
즉 Spring Security 를 이해하는데 있어 제일 중요한 포인트는 인증 (Authentication) 과 허가 (Authorization) 입니다. 보안과 관련된 용어인 인증과 허가를 간단히 하면 아래와 같습니다.
인증 (Authentication) 은 '누구인지 증명하는 과정' 입니다.
허가 (Authorization) 는 '권한이 있는지 확인하는 과정' 입니다.
Spring Security 를 설명하기에 앞서 FilterChain
에 대한 이해가 선행되어야 해서 설명드리도록 하겠습니다.
모든 HTTP 통신이 거쳐야 하는 FilterChain
아래 그림처럼 Client 가 보내는 모든 HTTP 통신은 Controller
가 있는 Servlet 에 도달하기 전에 FilterChain
을 거치고, Servlet 이 보내는 모든 HTTP 통신도 다시 FilterChain
을 거쳐 나가게 됩니다.
Spring Security 는 아래 그림처럼 HTTP 통신을 통제하는 FilterChain
에 SecurityFilterChain
을 삽입하여 모든 HTTP request 와 response 사이에 Spring Security 가 개입할 수 있게 합니다.
SecurityFilterChain은
interface
이며 따로 구현하지 않으면 자동으로DefaultSecurityFilterChain
클래스가 적용됩니다.
SecurityFilterChain
은 FilterChain
과 마찬가지로 내부적으로 필터 목록을 가지고 있고, 각 필터의 doFilter 메서드로 ServletRequest
와 ServletResponse
를 매개변수로 받아 모든 요청과 응답을 필터링합니다. 예를 들어, doFilter
메서드에서 HttpServletRequest
가 적절한지 검사하거나, HTTP Header 를 추가 / 삭제 / 변경할 수 있습니다. 심지어, 다음 필터로 더이상 진행하지 못하도록 하여 통신을 막을 수도 있습니다. 이렇게 Spring Security 는 SecurityFilterChain
으로 얻은 HTTP 통신에 대한 막강한 권한으로 인증 과 허가 를 수행해 악의적인 공격으로 부터 서버를 안전하게 보호합니다.
정리하자면, Spring Security 는
SecurityFilterChain
으로 인증과 허가를 수행합니다.
아래 목록은 SecurityFilterChain
에서 사용할 수 있는 필터 목록 일부입니다.
- CorsFilter
- CsrfFilter
- ExceptionTranslationFilter
- BasicAuthenticationFilter
- OAuth2LoginAuthenticationFilter
- OAuth2AuthorizationRequestRedirectFilter
- SessionManagementFilter
SecurityFilterChain
에 모든 필터를 직접 구현하여 Spring Security 를 만들 수도 있지만, 최대한 Spring 이 제공하는 기본 클래스를 사용합시다!
아래와 같이 WebSecurityConfigurerAdapter
클래스를 상속하고 configure
메서드를 Override 하여, 필요에 따라 필터를 추가하거나 조정하여 인증 과 허가 과정에 변화를 줄 수 있습니다.
@Configuration
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
httpSecurity
.cors() // CorsFilter 설정
.and()
csrf() // CsrfFilter 설정
}
}
인증 Authentication
Client 가 처음으로 Spring Security 가 보호하는 경로로 접근하면 이전에 인증했던 기록이 없기 때문에 Spring Security 는 인증을 수행하게 됩니다.
절차를 설명 드리면 Client 의 Request 가 FilterChain
을 거치던 중 SecurityFilterChain
에서 걸리게 됩니다. 즉 필터 목록 중 하나에서 AccessDeniedException
이나 AuthenticationException
예외를 발생시키고, SecurityFilterChain
의 필터 중 하나인 ExceptionTranslationFilter의 doFilter
메서드에서 이전 필터에서 발생시킨 예외에 따라 처리 방법을 결정합니다.
인증을 수행하기 위해
ExceptionTranslationFilter
는SecurityFilterChain
에 반드시 포함되어야 하는 필터입니다.
이전 필터가 진행되면서 AuthenticationException
예외가 발생한 2번의 경우, AuthenticationEntryPoint의 commence
메서드를 호출하여, 인증을 시작합니다.
개발자는 아래와 같은 방식으로 AuthenticationEntryPoint
를 구현합니다.
@Override
protected void configure(final HttpSecurity httpSecurity) throws Exception {
httpSecurity
.exceptionHandling( exceptionHandling -> exceptionHandling
.authenticationEntryPoint(myAuthenticationEntryPoint) // 인증을 수행할 AuthenticationEntryPoint
)
}
@Component
public class MyAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response,
AuthenticationException authException) throws IOException, ServletException {
// 인증 처리
}
}
AccessDeniedException
예외가 발생한 3번의 경우, AccessDeniedHandler의 handle
메서드를 호출하여 접근 제한 시나리오를 처리합니다.
만약 아무런 예외가 발생하지 않았다면,
ExceptionTranslationFilter
는 아무런 동작도 하지 않도록 설계되어 있습니다.
허가 Authorization
인증 과정을 수행한 뒤, Spring Security 는 인증된 사용자가 적절한 권한을 가지고 보호된 경로를 접근하는지 확인합니다.
허가 과정 역시 SecurityFilterChain
의 필터 목록 중 FilterSecurityInterceptor 로 시작하게 됩니다.
위 그림에서 제일 중요한 객체는 AccessDecisionManager
입니다. 이름에서 알 수 있듯이 허가 를 결정하는 객체이며, Authentication
, FilterInvocation
, ConfigAttributes
모두 AccessDecisionManager
가 허가 여부를 결정하는데 사용되는 객체입니다.
Authentication
은 현재 인증된 사용자를 의미하는 객체로,principal
과credentials
,authorities
속성을 가지고 있습니다.
실질적으로 AccessDecisionManager
의 decide
메서드에서 예외가 발생하지 않으면, 인증된 사용자의 접근이 허가 되었다 판단하고 SecurityFilterChain
을 지나 나머지 FilterChain
을 진행하게 됩니다.
'Develop > Backend 가이드' 카테고리의 다른 글
[Spring] JPA Mapping (0) | 2020.05.20 |
---|---|
[Spring] @NotNull, @NotEmpty, @NotBlank 차이 (0) | 2020.05.19 |
[Spring] VSCode에서 DevTools & LiveReload 사용하기 (2) | 2020.04.07 |
[Spring] 빈 Bean (0) | 2020.02.23 |
[Spring] 컨테이너 Container (0) | 2020.02.23 |
꾸준히 노력하는 개발자 "김예건" 입니다.