SecurityContextHolderFilter
SecurityContextHolderFilter 목적
- 이 필터는
DefaultSecurityFilterChain에 기본적으로 등록되며, 세 번째에 위치한다. - Security session 방식에서 주로 사용 된다.
- 이전 요청을 통해 인증된 사용자 정보를 현재 요청의
SecurityContextHolder의SecurityContext에 할당한다. - 현재 요청이 끝나면
SecurityContext를 초기화하여 다음 요청에서 불필요한 인증 정보를 제거한다. - 커스텀
SecurityFilterChain을 생성해도 기본적으로 등록되며, 비활성화하려면 아래와 같이 설정하면 된다.
Java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http.securityContext((context) -> context.disable());
return http.build();
}
- session 방식에서는 중요하게 사용 되고, JWT 토큰을 사용한 방식에서도 직접 사용하지 않더라도 마지막에
SecurityContext를 초기화 하는 구문이 있기 때문에 되도록 disable 하지 않는 게 좋다.
SecurityContextHolderFilter 클래스
Java
public class SecurityContextHolderFilter extends GenericFilterBean {
// ...
@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);
Supplier<SecurityContext> deferredContext = this.securityContextRepository.loadDeferredContext(request);
try {
this.securityContextHolderStrategy.setDeferredContext(deferredContext);
chain.doFilter(request, response);
}
finally {
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
}
// ...
}
기본 동작
- 사용자가 로그인하고 상태가
STATELESS가 아니라면, 서버의 세션 또는 외부 저장소(예: Redis)에 사용자 정보가 저장됨. - 저장된 사용자 정보를
SecurityContextRepository의loadDeferredContext()메서드를 통해 로드함. - 로드된
SecurityContext를SecurityContextHolder에 저장하고 다음 필터로 넘김. - 요청 처리가 완료되면
finally블록에서SecurityContextHolder를 초기화하여 인증 정보를 제거함. - 이전 필터에서
SecurityContext를 설정한 경우, 이를 유지하면서 필터 체인을 실행함.
SecurityContextRepository 인터페이스와 구현체
SecurityContextRepository는 사용자 정보를 저장 매체(세션, Redis 등)에서 불러오는 인터페이스다.- 구현체는 아래와 같다.
| 구현체 | 설명 |
|---|---|
HttpSessionSecurityContextRepository | 서버 세션 기반 저장소 |
NullSecurityContextRepository | 아무 작업을 수행하지 않음 (JWT 기반 STATELESS 관리 시 사용) |
RequestAttributeSecurityContextRepository | HttpServletRequest 속성 기반 저장소 |
| 기타 | 직접 구현 가능 |
커스텀 SecurityContextRepository 등록 방법
- 기본적으로
HttpSessionSecurityContextRepository가 사용되지만, 아래와 같이 설정하여 변경할 수 있다.
Java
@Bean
public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
http
.securityContext((context) -> context
.securityContextRepository(new RequestAttributeSecurityContextRepository()));
return http.build();
}
- JWT 기반
STATELESS방식에서는NullSecurityContextRepository를 사용할 수도 있다.
SecurityContextPersistenceFilter vs SecurityContextHolderFilter 차이
SecurityContextPersistenceFilter는SecurityContextHolderFilter의 이전 버전이다.- Spring Security 5.8 버전부터
SecurityContextHolderFilter로 변경되었으며, 기존 클래스는deprecated되었다.
변경된 부분
| 필터 | 설명 |
|---|---|
SecurityContextPersistenceFilter | 요청이 끝난 후 변경된 SecurityContext 를 저장소에 반영함. |
SecurityContextHolderFilter | SecurityContext 변경 사항을 저장하지 않음. |
변경된 코드 비교
SecurityContextPersistenceFilter
Java
finally{
SecurityContext contextAfterChainExecution = this.securityContextHolderStrategy.getContext();
this.securityContextHolderStrategy.clearContext();
this.repo.saveContext(contextAfterChainExecution, holder.getRequest(),holder.getResponse());
request.removeAttribute(FILTER_APPLIED);
this.logger.debug("Cleared SecurityContextHolder to complete request");
}
SecurityContext가 변경된 경우,SecurityContextRepository를 통해 저장소에 반영한다.
SecurityContextHolderFilter
Java
finally{
this.securityContextHolderStrategy.clearContext();
request.removeAttribute(FILTER_APPLIED);
}
- 요청이 끝나면
SecurityContext를 초기화하지만, 변경 사항을 저장하지 않는다.
변경에 따른 영향
SecurityContextHolderFilter는SecurityContext변경 사항을 저장하지 않으므로, 요청 중SecurityContext가 수정되면 세션이나 Redis 등의 저장소에는 반영되지 않는다.- 인증 상태를 지속적으로 유지하려면, 별도의 저장 로직을 구현해야 한다.