주의
이 책은 Spring Security 5 버전을 기준으로 작성되었으므로, Spring Boot 3.X 버전에서는 일부 클래스가 더 이상 사용되지(deprecated) 않을 수 있습니다.
서론
- 스프링 시큐리티의 HTTP 필터는 일반적으로 요청에 적용해야 하는 각 책임을 관리하며 책임의 체인을 형성함
- 필터는 요청을 수신하고 그 논리를 실행하며 최종적으로 체임의 다음 필터에 요청을 위임함

- 스프링 시큐리티는 맞춤 구성을 통해 필터 체인에 추가할 수 있는 필터 구현을 제공하지만 맞춤형 필터도 정의할 수 있음

1. 스프링 시큐리티 아키텍처의 필터 구현
- 인증 필터는 요청을 가로채고 인증 책임을 AuthenticationManager에 위임함
- 인증 이전에 특정 논리를 실행하려면 인증 필터 앞에 필터를 추가하면 됨
- 스프링 시큐리티 아키텍처의 필터는 일반적인 HTTP 필터이며 필터를 생성하기 위해 javax.servlet 패키지의 Filter 인터페이스를 구현하면 됨
- 다른 HTTP 필터와 마찬가지로 doFilter() 메서드를 재정의해 논리를 구현해야 함
- 해당 메서드는 ServletRequest, ServletResponse, 그리고 FilterChain 매개변수를 받음
- 필터 체인은 필터가 작동하는 순서가 정의된 필터의 모음
- 스프링 시큐리티에는 몇 가지 필터 구현과 순서가 있으며 이들 중 몇 가지를 소개하면 다음과 같음
- BasicAuthenticationFilter는 HTTP Basic 인증을 처리
- CsrfFilter는 CSRF (사이트 간 요청 위조)를 처리
- CorsFilter는 CORS (교차 출처 리소스 공유) 권한 부여 규칙을 처리
- 애플리케이션이 필터 체인에 이러한 모든 필터의 인스턴스를 반드시 가질 필요는 없음
- 필터 체인은 애플리케이션을 구성하는 방법에 따라 더 길어지거나 짧아질 수도 있음
- 개발자가 작성하는 구성에 따라 필터 체인의 정의가 영향을 받음
- i.g. HTTP Basic 인증 방식을 이용하려면 HttpSecurity 클래스의 httpBasic() 메서드를 호출해야 하는데 해당 메서드를 호출하면 필터 체인에 BasicAuthenticationFilter가 추가됨
2. 체인에서 기존 필터 앞에 필터 추가
- 모든 요청에 Request-Id 헤더가 있고 해당 헤더로 요청을 추적하여 인증을 수행하기 전에 헤더가 있는지 검증한다고 가정
- 인증 프로세스에는 DB 쿼리나 다른 리소스를 소비하는 작업이 포함될 수 있으므로 요청의 형식이 유효하지 않으면 이런 작업을 실행할 필요가 없음
- 위 요구 사항을 두 개의 단계로 해결할 수 있으며 최종 필터 체인 형상은 다음과 같음
- 필터를 구현: 요청에 필요한 헤더가 있는지 확인하는 RequestValidationFilter 클래스 생성
- 필터 체인에 필터 추가: 구성 클래스에서 configure() 메서드를 재정의해 필터 체인에 필터 추가

3. 체인에서 기존 필터 뒤에 필터 추가
- 간단한 로깅과 추적 목적을 위해 특정 인증 이벤트 이후 다른 시스템에 알림을 전달하는 사례가 있다고 가정
- 다음은 인증 필터 뒤에 성공한 인증 이벤트를 모두 추가하는 필터 예제

4. 필터 체인의 다른 필터 위치에 필터 추가
- 스프링 시큐리티의 기존 필터가 수행하는 책임에 대해 다른 구현을 제공할 때 필터 체인의 다른 필터 위치에 필터를 추가하는 과정이 적합함
- HTTP Basic 인증 흐름 대신 조금 다른 인증을 구현해서 애플리케이션이 사용자를 인증하기 위한 자격 증명으로 사용자 이름과 암호 대신 다른 접근법을 적용하기를 원한다고 가정했을 때 가능성이 있는 시나리오의 예는 다음과 같음
- 인증을 위한 정적 헤더 값에 기반을 둔 식별
- 대칭 키를 이용해 인증 요청 서명
- 인증 프로세스에 OTP 이용
4.1 인증을 위한 정적 헤더 값에 기반을 둔 식별
- 클라이언트는 HTTP 요청의 헤더에 항상 동일한 문자열 하나를 앱으로 전달
- 애플리케이션은 이러한 값을 예를 들어 DB나 비밀 볼트에 저장
- 애플리케이션은 해당 정적 값을 바탕으로 클라이언트를 식별
- 인증의 보안 수준은 낮지만 단순하다는 장점이 있어 설계자와 개발자는 백엔드 애플리케이션 간의 호출에 이를 자주 선택
- 암호와 서명을 적용하는 것처럼 복잡한 계산을 수행할 필요가 없기 때문에 빠르게 실행됨
- 정적 키를 인증에 이용하는 방식은 개발자가 보안 측면에서 인프라 수준에 더 의존하면서도 엔드포인트를 완전한 비보호 상태로 두지 않는 절충안

4.2 대칭 키를 이용해 인증 요청 서명
- 클라이언트와 서버가 모두 키의 값을 공유하므로 서로가 키 값을 아는 상태
- 클라이언트는 해당 키로 요청의 일부에 서명하고 서버는 같은 키로 서명이 유효한지 확인
- 서버는 각 클라이언트의 개별 키를 DB나 비밀 볼트에 저장할 수 있으며 비슷하게 비대칭 키 쌍을 이용할 수도 있음

4.3 인증 프로세스에 OTP 이용
- 문자 메시지를 통해 또는 Google Authenticator와 같은 인증 공급자 앱으로 OTP를 받음

4.4 맞춤형 필터 적용 예제
- 모든 요청에 대해 같은 정적 키 값을 이용한다고 가정
- 필터 클래스인 StaticKeyAuthenticationFilter는 속성 파일에서 정저 키 값을 읽고 Authorization 헤더 값과 같은지 확인
- 값이 같으면 필터는 요청을 필터 체인의 다음 구성 요소에 전달
- 그렇지 않으면 요청을 필터 체인에 전달하지 않고 응답의 HTTP 상태를 401 권한 없음으로 설정
- 중요한 점은 특정 위치에 필터를 추가해도 스프링 시큐리티는 해당 위치에 필터가 하나라고 가정하지 않음
- 필터 체인의 같은 위치에 필터를 더 추가할 수 있으며 이 경우 스프링 시큐리티는 필터가 실행되는 순서를 보장하지 않음
- 기존 필터의 위치에 다른 필터를 적용하면 필터가 대체된다고 생각하는 경우가 많은데 그렇지 않으며 필터 체인에 필요 없는 필터는 아예 추가하지 않아야 함
- 아래 예제에서는 필터 체인에 BasicAuthenticationFilter 인스턴스가 추가되는 것은 원하지 않으므로 HttpSecurity 클래스의 httpBasic() 메서드를 호출하지 않음

5. 스프링 시큐리티가 제공하는 필터 구현
- 스프링 시큐리티에는 Filter 인터페이스를 구현하는 여러 추상 클래스가 있으며, 이를 위해 필터 정의를 확장할 수 있음
- 또한 이러한 클래스는 구현을 확장할 때 이점을 얻을 수 있는 기능을 추가함
- OncePerRequestFilter는 GenericFilterBean을 확장하는 유용한 클래스
- 프레임워크는 필터 체인에 추가한 필터를 요청당 한 번만 실행하도록 보장하지 않음
- OncePerRequestFilter는 이름이 의미하듯이 필터의 doFilter() 메서드가 요청당 한 번만 실행되도록 논리를 구현
- OncePerRequestFilter 클래스에 관해 몇 가지 알아둘 사항은 다음과 같음
- 해당 클래스의 장점은 형식을 형 변환하여 HttpServletRequest 및 HttpServletResponse로 직접 요청을 수신한다는 것, Filter 인터페이스의 경우에는 요청과 응답을 형 변환해야 함
- 필터 체인에 추가한 필터가 특정 요청에는 적용되지 않는다고 결정할 수 있음, 이 경우 shouldNotFilter(HttpServletRequest) 메서드를 재정의하면 됨, 기본적으로 필터는 모든 요청에 적용됨
- OncePerRequestFilter는 기본적으로 비동기 요청이나 오류 발송 요청에는 적용되지 않음: 해당 동작을 변경하려면 shouldNotFilterAsyncDispatch() 및 shouldNotFilterErrorDispatch() 메서드를 재정의하면 됨
참고
스프링 시큐리티 인 액션
'Spring > Spring Security' 카테고리의 다른 글
| [Spring Security] 전역 메서드 보안 (0) | 2024.10.09 |
|---|---|
| [Spring Security] CSRF & CORS (0) | 2024.10.08 |
| [Spring Security] 필터 체인과 커스텀 필터 (0) | 2024.10.08 |
| [Spring Security] 액세스 제한과 권한 (0) | 2024.10.07 |
| [Spring Security] 아키텍처 간단 정리 (0) | 2024.09.13 |