Spring Security 5에서 6로 변경된 필터 확인하기(SecurityContextPersistenceFilter, SecurityContextHolderFilter)
- -
Spring Security 5 에서 6 버전으로 넘어가면서 변경된 SecurityContextPersistenceFilter 와 SecurityContextHolderFilter 를
알아보자
SecurityContextPersistenceFilter 인증 처리 과정
- 요청이 들어오면 SecurityContextRepository.loadContext() 를 호출해서 기존 인증 정보를 로딩한다.
=> 인증정보가 들어있는 SecurityContext를 가져옴 - 로딩된 SecurityContext를 SecurityContextHolderStrategy.setContext() 로 ThreadLocal 에 저장함
=> SecurityContextHolderStrategy 는 실제로 ThreadLocalSecurityContextHolderStrategy 클래스 사용 - 이후 필터 체인에서 인증 처리(UsernamePasswordAuthenticationFilter 등)가 해당 Context 사용
- 요청이 끝나면 finally 에서 SecurityContextHolderStrategy.getContext() 를 호출하여
해당 SecurityContext 를 가져와 SecurityContextRepository 에 saveContext() 한다. - SecurityContextHolderStrategy.clearContext() 를 통해 ThreadLocal 을 비운다.
@Deprecated
public class SecurityContextPersistenceFilter extends GenericFilterBean {
private SecurityContextRepository repo;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
public SecurityContextPersistenceFilter() {
this(new HttpSessionSecurityContextRepository());
}
public SecurityContextPersistenceFilter(SecurityContextRepository repo) {
this.repo = repo;
}
private void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)
throws IOException, ServletException {
// ensure that filter is only applied once per request
if (request.getAttribute(FILTER_APPLIED) != null) {
chain.doFilter(request, response);
return;
}
request.setAttribute(FILTER_APPLIED, Boolean.TRUE);
if (this.forceEagerSessionCreation) {
HttpSession session = request.getSession();
if (this.logger.isDebugEnabled() && session.isNew()) {
this.logger.debug(LogMessage.format("Created session %s eagerly", session.getId()));
}
}
HttpRequestResponseHolder holder = new HttpRequestResponseHolder(request, response);
SecurityContext contextBeforeChainExecution = this.repo.loadContext(holder);
try {
this.securityContextHolderStrategy.setContext(contextBeforeChainExecution);
if (contextBeforeChainExecution.getAuthentication() == null) {
logger.debug("Set SecurityContextHolder to empty SecurityContext");
}
else {
if (this.logger.isDebugEnabled()) {
this.logger
.debug(LogMessage.format("Set SecurityContextHolder to %s", contextBeforeChainExecution));
}
}
chain.doFilter(holder.getRequest(), holder.getResponse());
}
finally {
SecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();
// Crucial removal of SecurityContextHolder contents before anything else.
this.securityContextHolderStrategy.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(), holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
}
}
위 내용을 보자면 SecurityContextHolderStrategy 기본 전략인 ThreadLocal 방식의
ThreadLocalSecurityContextHolderStrategy 클래스를 사용해 ThreadLocal 에 직접 저장한다.
즉, 직접적으로 ThreadLocal 을 사용하는 구조이다.
SecurityContextHolderFilter 인증 처리과정
SecurityContextPersistenceFilter 는 deprecated 되었고, 대신 SecurityContextHolderFilter 가 등장했다.
해당 필터는 SecurityContextHolder 가 직접 ThreadLocal 을 건드리지 않도록 설계되었다.
SecurityContextHolderFilter 는 SecurityContextRepository 를 통해 컨텍스트를 처리하면서도
SecurityContextHolder 와는 간접적으로만 상호작용한다.
주요 변경사항은 SecurityContextHolderStrategy 의 전략 패턴을 이용해서 SecurityContextHolder가
ThreadLocal을 직접 접근하지 않도록 분리한다.
- SecurityContextRepository.loadDeferredContext(request) 를 통해 SecurityContext를 호출한다.
본인은 new HttpSessionSecurityContextRepository() 를 통해 설정했다. - SecurityContextHolderStrategy.setDeferredContext(SecurityContext) 를 통해
SecurityContext 를 ThreadLocal 에 저장한다. (기본전략이 ThreadLocal 방식) - 다음 인증필터내에서 SecurityContext 를 참조하여 인증 처리한다.
- SecurityContextHolderStrategy.clearContext() 를 호출하여 ThreadLocal 을 정리한다.
public class SecurityContextHolderFilter extends GenericFilterBean {
private final SecurityContextRepository securityContextRepository;
private SecurityContextHolderStrategy securityContextHolderStrategy = SecurityContextHolder
.getContextHolderStrategy();
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);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
chain.doFilter(request, response);
}
finally {
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
}
SecurityContextRepository 의 기본값은 HttpSessionSecurityContextRepository 이다.
.loadDeferredContext(request) 메서드를 통해 SecurtyContext 를 불러온다.
Session 내에 SpringSecurityContextKey 값으로 호출해 SecurityContext 를 불러온다.
SecurityContextHolderStrategy.setDeferredContext(seucirytContext) 를 통해
SecurityContext 를 저장하고 다음 인증필터로 간다.
인증필터가 종료된 이후 finally 를 통해 SecurityContextHolderStrategy의 clearContext() 호출하여
ThreadLocal 을 비운다.
'Java > Spring' 카테고리의 다른 글
Spring Boot log4j2 설정하기 (Spring Boot3, HikariCP) (1) | 2025.04.30 |
---|---|
Java ThreadLocal 이란? (2) | 2025.04.21 |
Spring Security 6 에서 AuthenticationSuccessHandler redirect Url 설정 (0) | 2025.04.21 |
Mybatis 의 Mapper 인터페이스 Bean 등록 방법 (0) | 2025.04.17 |
@Bean 메서드의 매개변수 종속성 주입 (0) | 2025.04.16 |
소중한 공감 감사합니다