Spring/Spring Security

[Spring Security] 1. Architecture

noahkim_ 2021. 7. 28. 11:01

1. A Review Of Filters

 

  • Spring Security는 Servlet Filter 기반으로 동작함

 

동작 방식

더보기
  1. 클라이언트: 요청 보냄
  2. 서블릿 컨테이너: 필터들을 묶어 FilterChain 생성
  3. 서블릿 컨테이너: 요청 경로를 기준으로 어떤 Filter들이 요청을 처리할지 결정.

 

Servlet Filter

  • HTTP 요청을 처리하기 위한 사전/사후 작업용 인터셉터

 

동작
구분 설명 결과
stop 이후의 필터나 서블릿에 요청을 전달하지 않음
Filter가 직접 응답 작성
modify HttpServletRequest 또는 HttpServletResponse를 수정한 뒤 전달
수정된 객체가 하위로 전달됨

 

FilterChain

  • 여러 개의 Servlet Filter가 연결된 객체
  • ✅ 요청을 다음 필터로 넘길지 결정할 수 있음

 

2. DelegatingFilterProxy

  • 서블릿 컨테이너와 Spring의 ApplicationContext 계층을 브리징해주는 Servlet Filter
  •  서블릿 컨테이너는 Spring 빈으로 관리되는 필터는 직접적으로 모름
  • ➡️ 이를 해결하기 위해 사용

 

동작 방식

더보기
  1. 서블릿 컨테이너: DelegatingFilterProxy를 서블릿 필터로 등록
  2. DelegatingFilterProxy: Spring Bean으로 등록된 FilterChainProxy에 필터 작업을 위임함

 

장점

장점 설명
Spring Bean 필터 사용 가능 DelegatingFilterProxy를 통해 Spring Bean으로 등록된 필터를 사용할 수 있음
지연 로딩 지원 실제 필터를 요청 시점에 처음 조회해서 캐싱함

 

3. FilterChainProxy

  • Spring Security가 사용하는 메인 서블릿 필터 (중심 진입점)
  • ✅ 들어온 요청을 여러 SecurityFilterChain 중, 적절한 SecurityFilterChain에 라우팅
  •  'springSecurityFilterChain' 이란 이름으로 빈 등록됨

 

4. SecurityFilterChain

  • 요청에 적용할 보안 필터 묶음
  •  FilterChainProxy에 등록되어 관리됨
  •  HttpSecurity DSL로 구성 함

 

RequestMatcher

  • HTTP 요청이 조건을 만족하는지 판단하는 인터페이스
  • ✅ 어떤 체인/규칙을 적용할지 결정할 때 사용됨
  •  여러개가 매칭되어도 첫 번쨰 매칭 체인만 실행됨
구현체 클래스 설명 사용 예시
AntPathRequestMatcher ANT 스타일 경로 매칭 (/api/**, /admin/*)
new AntPathRequestMatcher("/admin/**")
MvcRequestMatcher Spring MVC 경로 방식으로 매칭
new MvcRequestMatcher(handlerMappingIntrospector, "/admin")
RegexRequestMatcher 정규표현식 기반 매칭
new RegexRequestMatcher("/admin/.*", "GET")
OrRequestMatcher 여러 RequestMatcher 중 하나라도 일치하면 true
new OrRequestMatcher(matcher1, matcher2)
AndRequestMatcher 여러 RequestMatcher가 모두 일치해야 true
new AndRequestMatcher(matcher1, matcher2)
NegatedRequestMatcher 특정 Matcher의 결과를 반전 (not)
new NegatedRequestMatcher(matcher)

 

 

예시

더보기

기본 URL

http
  .securityMatcher("/admin/**") // URL 패턴만 기준
  .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

 

HTTP 메서드

http
  .securityMatcher(new AntPathRequestMatcher("/api/**", "POST")) // POST 요청만 매칭
  .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

 

커스텀 RequestMatcher (헤더 기반)

http
  .securityMatcher(request -> {
      String header = request.getHeader("X-Tenant-Id");
      return header != null && header.equals("tenant-123");
  })
  .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

 

요청 파라미터

http
  .securityMatcher(request -> "true".equals(request.getParameter("secure")))
  .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

 

SecurityFilterChain 정의

설정

더보기
@Configuration
@EnableWebSecurity
public class SecurityConfig {
    @Bean
    public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
        http
            .csrf(Customizer.withDefaults())
            .httpBasic(Customizer.withDefaults())
            .formLogin(Customizer.withDefaults())
            .authorizeHttpRequests(auth -> auth.anyRequest().authenticated());

        return http.build();
    }
}
필터 이름 DSL 방식
CsrfFilter http.csrf()
BasicAuthenticationFilter http.httpBasic()
UsernamePasswordAuthenticationFilter http.formLogin()
AuthorizationFilter
http.authorizeHttpRequests()

 

5. Security Filters

종류

역할 구분 설명 대표 필터 예시
Exploit 보호 웹 기반 공격 방지 (CSRF, CORS, 클릭재킹, HSTS 등)
CsrfFilter
HeaderWriterFilter
CorsFilter
인증 사용자의 신원을 확인하고 SecurityContext에 인증 정보 저장
UsernamePasswordAuthenticationFilter
BasicAuthenticationFilter
인가 인증된 사용자가 요청한 리소스에 접근할 수 있는지 권한 확인 AuthorizationFilter
(AccessDecisionManager와 연결됨)

 


기본 필터 구성

더보기
DisableEncodeUrlFilter,
WebAsyncManagerIntegrationFilter,
SecurityContextHolderFilter,
HeaderWriterFilter,
CorsFilter,
CsrfFilter,
LogoutFilter,
BasicAuthenticationFilter,
RequestCacheAwareFilter,
SecurityContextHolderAwareRequestFilter,
AnonymousAuthenticationFilter,
ExceptionTranslationFilter,
AuthorizationFilter

 

필터 설명
DisableEncodeUrlFilter
URL에 세션 ID를 붙이는 기능 제거
WebAsyncManagerIntegrationFilter
비동기 요청 처리 시 SecurityContext 전달
SecurityContextHolderFilter
SecurityContext 저장 및 정리
HeaderWriterFilter
X-Frame-Options 등 보안 헤더 설정
CorsFilter
CORS 처리 (pre-flight 요청 등)
CsrfFilter CSRF 토큰 검증
LogoutFilter
로그아웃 URL 요청 처리
RequestCacheAwareFilter
인증 후 원래 요청으로 리다이렉트
SecurityContextHolderAwareRequestFilter
HttpServletRequest 확장 (getUserPrincipal 등 지원)
AnonymousAuthenticationFilter
인증되지 않은 사용자를 익명 사용자로 처리
ExceptionTranslationFilter
인증/인가 실패 시 예외를 처리하고 리다이렉트 또는 응답 전환
AuthorizationFilter
인가 검사 수행

 

 

Adding a Custom Filter

추가 방식
메서드 설명
addFilterBefore()
지정 필터 이전에 추가
addFilterAfter()
지정 필터 이후에 추가
addFilterAt() 지정 필터 대체

 

필터 위치 정하기
필터 목적 이 필터 뒤에 추가 이유
Exploit 보호 필터 SecurityContextHolderFilter
컨텍스트 로드 후 실행해야 하므로
인증 필터 LogoutFilter
보안 컨텍스트 + 보호 이후
인가 필터 AnonymousAuthenticationFilter
인증까지 끝난 뒤에 실행해야 함

 

Bean으로 필터 등록 시 주의사항

  • 빈으로 등록된 필터는 자동으로 Servlet Filter로도 등록됨
  • ✅ SecurityFilterChain에 추가할 경우 중복 실행됨
  • ➡️ Servlet 등록을 막음으로써 중복 실행을 해결할 수 있음

 

예제) Servlet 등록 막기

더보기
@Bean
public FilterRegistrationBean<CustomFilter> customFilterRegistration(CustomFilter filter) {
    FilterRegistrationBean<CustomFilter> registration = new FilterRegistrationBean<>(filter);
    registration.setEnabled(false); // 서블릿 컨테이너 등록 방지
    return registration;
}

 

Customizing a Spring Security Filter

예) 수동 등록

더보기
BasicAuthenticationFilter basic = new BasicAuthenticationFilter(...);
http.addFilterAt(basic, BasicAuthenticationFilter.class);

 

예) 비활성화

더보기
http.httpBasic(basic -> basic.disable()); // 필터 비활성화

 

6. Handling Security Exceptions

ExceptionTranslationFilter

  • 인증, 인가 예외를 처리하기 위한 전용 필터
예외 유형 의미 예외 처리
AuthenticationException 인증되지 않은 사용자가 보호된 리소스에 접근하려 할 때 발생
SecurityContextHolder 비움
Request 저장 (재시도용)
AuthenticationEntryPoint 호출
- 리다이렉션 (로그인 페이지 등)
- 401 응답코드 반환
AccessDeniedException 인증은 되었지만 권한이 없는 사용자가 접근할 때 발생
AccessDeniedHandler 호출
- 403 응답코드 반환

 

7. Saving Requests Between Authentication

  • 인증되지 않은 사용자가 로그인해야 접근 가능한 페이지를 요청했을 경우
  • ✅ 로그인 후 다시 그 요청으로 돌아가기 위해 요청을 임시 저장해둬야 함

 

RequestCacheAwareFilter

  • 인증 성공 후, 저장된 요청을 RequestCache에서 꺼내 재시도하는 필터
  •  로그인 성공 시, 원래 요청을 꺼내 처리함

 

RequestCache

  • 인증 필요 요청을 저장하는 인터페이스
구현체 의미
HttpSessionRequestCache 기본 구현체
NullRequestCache 저장을 원하지 않을 경우 사용하는 구현체

 

ex) 특정 파라미터가 있을때만 요청 저장

더보기
@Bean
DefaultSecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
    HttpSessionRequestCache requestCache = new HttpSessionRequestCache();
    requestCache.setMatchingRequestParameterName("continue");

    http.requestCache(cache -> cache.requestCache(requestCache));
    return http.build();
}

 

ex) 저장 안하기

더보기
@Bean
SecurityFilterChain springSecurity(HttpSecurity http) throws Exception {
    http.requestCache(cache -> cache.requestCache(new NullRequestCache()));
    return http.build();
}

 

8. Logging

  • 필터 체인 로그를 제공함
로그 레벨 확인 내용 사용 목적
DEBUG 어떤 필터들이 전체적으로 등록되어 있는지 애플리케이션 시작 시, SecurityFilterChain 구성 확인
TRACE 특정 요청에 대해 각 필터가 언제 실행되었는지 요청 처리 과정에서 어느 필터가 실행됐고 어디서 차단됐는지 확인

 

 

참고