🌀
f1v3-log
  • Welcome
  • 개발
    • SecurityContext를 새로 만들어야 할까?
    • OAuth2AuthorizationRequestResolver 커스터마이징
    • 동시성 문제를 해결해보자
    • MySQL은 어떻게 ID 값을 순차적으로 넣어주는 것일까? (Feat. Auto Increment Lock)
    • 외부 API 호출에 대한 고찰
      • HTTP Clients in Spring Boot
      • I/O와 트랜잭션 분리하기
      • 처리율 제한 장치 (Rate Limiter) 도입
      • 외부 API 의존성을 줄여보자
      • 캐시 레이어를 구성해보자 (Local Cache)
    • JPA Deep Dive
      • 결제 및 정산 시스템 기능 요구사항 분석
      • 글로벌 서비스를 고려할 때, 타임존 이슈를 어떻게 처리해야 할까?
      • Spring Data JPA - ID 생성 전략과 채번은 어떻게 되는걸까?
  • 회고
    • NHN Academy 인증과정 회고
    • DND 11기 회고
  • 독서
    • Effective Java 3/E
      • Item 1. 생성자 대신 정적 팩터리 메서드를 고려하라
      • Item 2. 생성자에 매개변수가 많다면 빌더를 고려하라
      • Item 3. private 생성자나 열거 타입으로 싱글턴임을 보증하라
    • 객체지향의 사실과 오해
      • 1장. 협력하는 객체들의 공동체
      • 2장. 이상한 나라의 객체
      • 3장. 타입과 추상화
      • 4장. 역할, 책임, 협력
      • 5장. 책임과 메시지
      • 6장. 객체 지도
      • 7장. 함께 모으기
  • Real MySQL 8.0
    • 04. 아키텍처
    • 05. 트랜잭션과 잠금
    • 08. 인덱스
    • 09. 옵티마이저와 힌트
  • 생각정리
    • 기술에 매몰되지 말자.
  • 공부
    • 객체지향 5원칙(SOLID)
      • SRP (Single Responsibility Principle)
      • OCP (Open Closed Principle)
Powered by GitBook
On this page
  • 궁금했던 점
  • 결론
  • 필터 체인의 동작
  • SecurityContextHolderFilter
  • 그렇다면, 어떤 방식이 맞는 방식일까?

Was this helpful?

  1. 개발

SecurityContext를 새로 만들어야 할까?

궁금했던 점

JWT 검증 필터 부분 코드를 리팩토링하면서 다음과 같은 의문이 들었다.

// 1번 방법 : 새로운 SecurityContext 생성
SecurityContext securityContext = SecurityContextHolder.createEmptyContext();
securityContext.setAuthentication(authToken);
SecurityContextHolder.setContext(securityContext);

// 2번 방법 : 기존 SecurityContext 사용
SecurityContextHolder.getContext().setAuthentication(authToken);

대부분의 JWT 검증 필터 구현 코드를 찾아보면, 2번 방법을 사용하는 것으로 보여진다. 또한, 우리 프로젝트에서는 1번 방법으로 구현을 했었지만, 2번 방법으로 변경해도 아무런 문제가 없이 동작한다.

여기서 드는 의문은,

  • 어느 시점에 SecurityContextHolder 에 SecurityContext 를 생성하고 넣어준 것인가?

  • 1번 방법이 아닌 2번 방법을 사용하는 이유는 뭘까?


결론

2번 방식을 통해 SecurityContextHolder.getContext() 를 호출하여 기존 SecurityContext를 사용하는 방식으로 진행하게 되었다.

왜 2번을 선택했고 근거에 대해서 하나씩 찾아보자.

필터 체인의 동작

필터 체인이 어떻게 동작하는지 파악하고, 어디서 SecurityContext가 생성되는지 파악을 하는 것이 우선이라고 판단했다. 필터 체인이 어떻게 구성이 되어있는지 파악하기 위해 @EnableWebSecurity(debug = true) 을 SecurityConfig 클래스에 설정해준 후 확인해보자.

@Configuration
@RequiredArgsConstructor
@EnableWebSecurity(debug = true)
public class SecurityConfig {

    // ...
    
}

Filter Chain 동작

Security filter chain: [
  DisableEncodeUrlFilter
  WebAsyncManagerIntegrationFilter
  SecurityContextHolderFilter  // (1)
  HeaderWriterFilter
  CorsFilter
  LogoutFilter
  OAuth2AuthorizationRequestRedirectFilter
  OAuth2LoginAuthenticationFilter
  JwtAuthenticationFilter   
  RequestCacheAwareFilter
  SecurityContextHolderAwareRequestFilter
  AnonymousAuthenticationFilter
  SessionManagementFilter
  ExceptionTranslationFilter
  AuthorizationFilter
]

여기서 우리가 주목해야 하는 필터는 (1) SecurityContextHolderFilter 이다. 해당 필터는 이름에서 알 수 있듯이 SecurityContextHolder 와 관련된 작업을 하는 필터이다.

SecurityContextHolderFilter

필터의 내부 구조를 파악해보자.

필터의 doFilter 메서드

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		doFilter((HttpServletRequest) request, (HttpServletResponse) response, chain);
	}

	private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
			throws ServletException, IOException {
		if (request.getAttribute(FILTER_APPLIED) != null) {
			chain.doFilter(request, response);
			return;
		}
		request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
		// 1. Context Load 
		Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
		try {
			// 2. Set Context
			this.securityContextHolderStrategy.setDeferredContext(deferredContext);
			chain.doFilter(request, response);
		}
		finally {
			this.securityContextHolderStrategy.clearContext();
			request.removeAttribute(FILTER_APPLIED);
		}
	}

위 코드에서 집중해서 봐야 할 부분은

  1. securityContextRepository 에서 컨텍스트를 불러오고

  2. securityContextHolderStrategy 를 통해 컨텍스트를 저장

하는 부분이다.

SecurityContext 로딩 및 설정:

Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
        this.securityContextHolderStrategy.setDeferredContext(deferredContext);
        chain.doFilter(request, response);
}
  • 현재 요청에 대한 SecurityContext를 로드합니다.

  • SecurityContextHolderStrategy를 사용하여 SecurityContext를 설정하며, 기본적으로 ThreadLocal을 사용합니다.

  • 이후, chain.doFiler() 를 통해 다음 필터로 요청을 전달합니다.

이렇게 알 수 있듯이, SecurityContextHolderFilter 에서 미리 사용할 SecurityContext 를 생성하고 SecurityContextHolder 에서 들고있는 것을 알 수 있다.

그렇다면, 어떤 방식이 맞는 방식일까?

정답은 없는 것 같다.

하지만, 현 시점 우리 프로젝트에서는 JWT 검증 필터 이전까지 SecurityContext 에 접근해서 Authentication 정보를 넣는 작업을 하지 않고 있다. 즉, 초기의 상태를 유지하고 있는 상태로 JWT 검증 필터까지 오게 되는 것이다.

이러한 상황에서는 굳이 새로운 컨텍스트를 생성(SecurityContext)하고 SecurityContextHolder 에 넣어주는 작업은 불필요할 것이라고 판단이 된다.

물론, 현재 컨텍스트에 필요한 정보가 담겨져 있을 경우 (새로운 필터가 도입되는 등의 문제로) 원하지 않는 동작을 할 것이라고 판단이 된다.

PreviousWelcomeNextOAuth2AuthorizationRequestResolver 커스터마이징

Last updated 6 months ago

Was this helpful?