1. A Review Of Filters
- Spring Security는 Servlet Filter 기반으로 동작함
서블릿 동작 원리
- 클라이언트가 요청을 보냄
- 서블릿 컨테이너: 요청 경로를 기준으로 어떤 Filter들이 요청을 처리할지 결정.
- 서블릿 컨테이너: 필터들을 묶어 FilterChain 생성
Servlet Filter
- HTTP 요청을 처리하기 위한 사전/사후 작업용 인터셉터
동작
구분 | 설명 | 결과 |
stop | 이후의 필터나 서블릿에 요청을 전달하지 않음 |
Filter가 직접 응답 작성
|
modify | HttpServletRequest 또는 HttpServletResponse를 수정한 뒤 전달 |
수정된 객체가 하위로 전달됨
|
FilterChain
- 여러 개의 Servlet Filter가 연결된 객체
- 요청을 다음 필터로 넘길지 결정할 수 있음
2. DelegatingFilterProxy
- 서블릿 컨테이너의 생명주기와 Spring의 ApplicationContext 사이를 연결해주는 Servlet Filter 구현체
- 서블릿 컨테이너는 Spring Filter를 인식하지 못함 → 이를 해결하기 위해 사용
동작 방식
- 서블릿 컨테이너에 DelegatingFilterProxy를 서블릿 필터로 등록
- DelegatingFilterProxy는 내부적으로 Spring Bean으로 등록된 FilterChainProxy를 찾아 위임함
- FilterChainProxy은 springSecurityFilterChain 이란 이름으로 등록되어 있음
장점
장점 | 설명 |
Spring Bean 필터 사용 가능 | DelegatingFilterProxy를 통해 Spring Bean으로 등록된 필터를 사용할 수 있음 |
지연 로딩 지원 | Spring의 ContextLoaderListener는 컨테이너가 시작되고 나서야 ApplicationContext를 로딩함 → DelegatingFilterProxy는 실제 필터를 나중에 로딩하므로 이 타이밍 문제를 해결할 수 있음 |
3. FilterChainProxy
- Spring Security가 사용하는 메인 필터 (중심 진입점)
- 여러 SecurityFilterChain 중, 요청에 적절한 SecurityFilterChain에 라우팅하여 처리
4. SecurityFilterChain
- 요청에 적용할 보안 필터들의 묶음
- HttpSecurity DSL로 구성됨
- 이 필터체인은 모두 FilterChainProxy에 등록되어 관리됨
RequestMatcher
- HTTP 요청이 특정 조건에 부합하는지 판단하는 인터페이스
- SecurityFilterChain 설정 시, 특정 요청에 보안 설정을 적용할지 여부를 결정할 때 사용됨
구현체 클래스 | 설명 | 사용 예시 |
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()
|
기본 필터 구성
더보기
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 |
인가(Authorization) 검사 수행
|
여러 개의 SecurityFilterChain 매칭
- 여러개가 매칭되어도 첫 번쨰 매칭 체인만 실행됨
5. Security Filters
종류
역할 구분 | 설명 | 대표 필터 예시 |
Exploit 보호 | 웹 기반 공격 방지 (CSRF, CORS, 클릭재킹, HSTS 등) |
CsrfFilter
HeaderWriterFilter CorsFilter |
인증 | 사용자의 신원을 확인하고 SecurityContext에 인증 정보 저장 |
UsernamePasswordAuthenticationFilter
BasicAuthenticationFilter |
인가 | 인증된 사용자가 요청한 리소스에 접근할 수 있는지 권한 확인 | AuthorizationFilter (AccessDecisionManager와 연결됨) |
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 | 특정 요청에 대해 각 필터가 언제 실행되었는지 | 요청 처리 과정에서 어느 필터가 실행됐고 어디서 차단됐는지 확인 |
참고
'Spring > Spring Security' 카테고리의 다른 글
[Spring Security] 3-4. Authentication: logout (0) | 2023.10.03 |
---|---|
[Spring Security] 3-3. Authentication: Password Storage (0) | 2023.10.02 |
[Spring Security] 3-2. Authentication: Username/Password (2) | 2023.10.02 |
[Spring Security] 5. 예외 처리 (0) | 2023.09.30 |
[Spring Security] 3-1. Authentication: Architecture (0) | 2021.07.28 |