인증 상태 영속성
SecurityContext의 저장, 로드, 삭제 메커니즘과 요청 간 인증 유지 전략
목차
1. SecurityContextRepository
개념
SecurityContextRepository는 사용자가 인증을 한 이후 요청에 대해 계속 사용자의 인증을 유지하기 위해 사용되는 인터페이스다.
인증 요청과 인증 후 요청의 차이
GET /login"]:::client AF["AuthenticationFilter"]:::filter SC1["SecurityContext
+ Authentication"]:::sc SCR1["SecurityContextRepository"]:::repo HS1["HttpSession
SecurityContext 저장"]:::session C1 --> AF AF -->|"인증 완료"| SC1 SC1 -->|"명시적으로 저장"| SCR1 SCR1 --> HS1 end subgraph AFTER_REQ["인증 후 요청"] direction TB C2["Client
GET /user"]:::client SCHF["SecurityContextHolderFilter"]:::filter SCR2["SecurityContextRepository"]:::repo HS2["HttpSession"]:::session SC2["SecurityContext
+ Authentication"]:::sc C2 --> SCHF SCHF -->|"컨텍스트 로드"| SCR2 SCR2 --> HS2 HS2 -->|"세션에서 가져옴"| SC2 end classDef client fill:#1c3a5f,stroke:#58a6ff,color:#ffffff classDef filter fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef sc fill:#1a3a2a,stroke:#3fb950,color:#ffffff classDef repo fill:#5c3d00,stroke:#d29922,color:#ffffff classDef session fill:#2d333b,stroke:#768390,color:#e6edf3
SecurityContextRepository 인터페이스
| 메서드 | 설명 |
|---|---|
containsContext(HttpServletRequest) | 현재 사용자를 위한 보안 컨텍스트가 저장소에 있는지 여부를 조회 |
saveContext(SecurityContext, HttpServletRequest, HttpServletResponse) | 인증 요청 완료 시 보안 컨텍스트를 저장한다 |
loadDeferredContext(HttpServletRequest) | 로딩을 지연시켜 필요 시점에 SecurityContext를 가져온다 |
구현체
| 구현체 | 설명 |
|---|---|
HttpSessionSecurityContextRepository | 요청 간에 HttpSession에 보안 컨텍스트를 저장한다. 후속 요청 시 컨텍스트 영속성을 유지한다 |
RequestAttributeSecurityContextRepository | ServletRequest에 보안 컨텍스트를 저장한다. 후속 요청 시 컨텍스트 영속성을 유지할 수 없다 |
NullSecurityContextRepository | 세션을 사용하지 않는 인증(JWT, OAuth2)일 경우 사용하며 컨텍스트 관련 아무런 처리를 하지 않는다 |
DelegatingSecurityContextRepository | RequestAttribute와 HttpSession 두 Repository를 동시에 사용할 수 있도록 위임하는 클래스. 초기화 시 기본으로 설정된다 |
2. SecurityContextHolderFilter
개념
SecurityContextHolderFilter는 SecurityContextRepository를 사용하여 SecurityContext를 얻고 이를 SecurityContextHolder에 설정하는 필터 클래스다.
이 필터는 SecurityContextRepository.saveContext()를 강제로 실행시키지 않고, 사용자가 명시적으로 호출해야 SecurityContext를 저장할 수 있다. 이 점이 SecurityContextPersistenceFilter와 다르다.
인증이 지속되어야 하는지를 각 인증 메커니즘이 독립적으로 선택할 수 있게 하여 더 나은 유연성을 제공하고, HttpSession에 필요할 때만 저장함으로써 성능을 향상시킨다.
SecurityContext 생성, 저장, 삭제 시나리오
| 시나리오 | 동작 |
|---|---|
| 익명 사용자 | SecurityContextRepository로 새로운 SecurityContext를 생성하여 SecurityContextHolder에 저장 후 다음 필터로 전달. AnonymousAuthenticationFilter에서 AnonymousAuthenticationToken을 SecurityContext에 저장 |
| 인증 요청 | SecurityContextRepository로 새로운 SecurityContext를 생성하여 SecurityContextHolder에 저장 후 다음 필터로 전달. UsernamePasswordAuthenticationFilter에서 인증 성공 후 SecurityContext에 Authentication 저장. SecurityContextRepository를 사용하여 HttpSession에 SecurityContext 저장 |
| 인증 후 요청 | SecurityContextRepository를 사용하여 HttpSession에서 SecurityContext를 꺼내어 SecurityContextHolder에 저장 후 다음 필터로 전달. SecurityContext 안에 Authentication 객체가 존재하면 인증 유지 |
| 클라이언트 응답 시 | SecurityContextHolder.clearContext()로 컨텍스트를 삭제한다. 스레드 풀 환경에서는 반드시 필요 |
SecurityContextHolderFilter 흐름
request"]:::client --> SCHF["SecurityContextHolderFilter"]:::filter SCHF --> SCR["SecurityContextRepository"]:::repo SCR --> HS["HttpSession"]:::session HS --> CHK{"SecurityContext
존재하는가?"} CHK -->|"Y"| EXIST["SecurityContextHolder
SecurityContext + Authentication"]:::success CHK -->|"N"| NEW["SecurityContextHolder
SecurityContext (null)"]:::empty EXIST --> CHAIN["chain.doFilter()"]:::chain NEW --> CHAIN CHAIN --> AF["AuthenticationFilter"]:::filter AF -->|"인증 성공"| SC_AUTH["SecurityContext
+ Authentication"]:::success SC_AUTH --> SCR2["SecurityContextRepository"]:::repo SCR2 -->|"세션에 저장"| HS2["HttpSession
SecurityContext"]:::session CHAIN --> CLEAR["SecurityContextHolder
clearContext()"]:::fail classDef client fill:#1c3a5f,stroke:#58a6ff,color:#ffffff classDef filter fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef repo fill:#5c3d00,stroke:#d29922,color:#ffffff classDef session fill:#2d333b,stroke:#768390,color:#e6edf3 classDef success fill:#1a3a2a,stroke:#3fb950,color:#ffffff classDef empty fill:#5c1a1a,stroke:#f85149,color:#ffffff classDef chain fill:#2d333b,stroke:#768390,color:#e6edf3 classDef fail fill:#5c1a1a,stroke:#f85149,color:#ffffff
3. SecurityContextHolderFilter vs SecurityContextPersistenceFilter
| 항목 | SecurityContextHolderFilter | SecurityContextPersistenceFilter |
|---|---|---|
| SecurityContext 로드 | SecurityContextRepository에서 로드 | SecurityContextRepository에서 로드 |
| SecurityContext 저장 | 저장하지 않음 (사용자가 명시적으로 호출) | 응답 시점에 Session에 자동 저장 |
| 상태 | 현재 사용 (권장) | Deprecated |
SecurityContextPersistenceFilter는 Deprecated 되었기 때문에 레거시 시스템 외에는 SecurityContextHolderFilter를 사용한다.
4. securityContext() API
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http.securityContext(securityContext -> securityContext
.requireExplicitSave(true) // SecurityContext를 명시적으로 저장할 것인지 여부 설정
// 기본값은 true
// true이면 SecurityContextHolderFilter 사용
// false이면 SecurityContextPersistenceFilter 사용
);
return http.build();
}
requireExplicitSave(true)가 기본값이다. 이 설정에서는 인증 필터가 인증 성공 후 직접 SecurityContextRepository.saveContext()를 호출해야 SecurityContext가 세션에 저장된다.
5. 커스텀 인증 필터에서의 SecurityContextRepository
개념
커스텀 인증 필터를 구현할 경우 인증이 완료된 후 SecurityContext를 SecurityContextHolder에 설정한 후 SecurityContextRepository에 저장하기 위한 코드를 명시적으로 작성해야 한다.
// 커스텀 인증 필터에서 SecurityContext 저장 (필수)
securityContextHolderStrategy.setContext(context);
securityContextRepository.saveContext(context, request, response);
securityContextRepository는 HttpSessionSecurityContextRepository 또는 DelegatingSecurityContextRepository를 사용하면 된다.
6. 스프링 MVC 로그인 구현
개념
스프링 시큐리티 필터에 의존하는 대신 수동으로 사용자를 인증하는 경우 스프링 MVC 컨트롤러 엔드포인트를 사용할 수 있다. 요청 간에 인증을 저장하고 싶다면 HttpSessionSecurityContextRepository를 사용하여 인증 상태를 저장할 수 있다.
SecurityContextRepository securityContextRepository =
new HttpSessionSecurityContextRepository();
@PostMapping("/login")
public void login(@RequestBody LoginRequest loginRequest,
HttpServletRequest request,
HttpServletResponse response) {
// 1. 사용자 이름과 비밀번호를 담은 인증 객체를 생성한다
UsernamePasswordAuthenticationToken token =
UsernamePasswordAuthenticationToken.unauthenticated(
loginRequest.getUsername(), loginRequest.getPassword());
// 2. 인증을 시도하고 최종 인증 결과를 반환한다
Authentication authentication =
authenticationManager.authenticate(token);
// 3. 인증 결과를 컨텍스트에 저장한다
SecurityContext securityContext =
SecurityContextHolder.getContextHolderStrategy().createEmptyContext();
securityContext.setAuthentication(authentication);
// 4. 컨텍스트를 ThreadLocal에 저장한다
SecurityContextHolder.getContextHolderStrategy()
.setContext(securityContext);
// 5. 컨텍스트를 세션에 저장해서 인증 상태를 영속한다
securityContextRepository.saveContext(
securityContext, request, response);
}
각 단계별 역할
(username + password)"]:::token_before A --> B["② AuthenticationManager.authenticate()
인증 시도 및 결과 반환"]:::manager B --> C["③ SecurityContext.setAuthentication()
인증 결과를 컨텍스트에 저장"]:::process C --> D["④ SecurityContextHolder.setContext()
컨텍스트를 ThreadLocal에 저장"]:::process D --> E["⑤ SecurityContextRepository.saveContext()
컨텍스트를 세션에 저장하여 영속"]:::success classDef token_before fill:#5c3d00,stroke:#d29922,color:#ffffff classDef manager fill:#3d1f5c,stroke:#bc8cff,color:#ffffff classDef process fill:#2d333b,stroke:#768390,color:#e6edf3 classDef success fill:#1a3a2a,stroke:#3fb950,color:#ffffff
saveContext)를 빠뜨리면 인증은 성공하지만 세션에 저장되지 않아 다음 요청에서 인증이 유지되지 않는다. SecurityContextHolderFilter는 자동 저장을 하지 않으므로 반드시 명시적으로 호출해야 한다.
정리
- SecurityContextRepository — SecurityContext를 저장하고 로드하는 인터페이스. 기본 구현체로 HttpSessionSecurityContextRepository(세션 저장)와 DelegatingSecurityContextRepository(기본 설정)가 있다
- SecurityContextHolderFilter — SecurityContextRepository에서 SecurityContext를 로드하여 SecurityContextHolder에 설정하는 필터. saveContext()를 자동으로 호출하지 않으므로 명시적 저장이 필요하다
- SecurityContextPersistenceFilter — 응답 시점에 자동으로 세션에 저장하던 레거시 필터. 현재 Deprecated
- requireExplicitSave(true) — 기본값. SecurityContextHolderFilter를 사용하며, 인증 필터가 직접 saveContext()를 호출해야 한다
- 커스텀 인증 필터 — 인증 완료 후 반드시 SecurityContextHolder에 설정하고 SecurityContextRepository에 명시적으로 저장해야 한다
- 스프링 MVC 로그인 — 필터 대신 컨트롤러에서 직접 인증할 때도 SecurityContextRepository.saveContext()를 호출해야 요청 간 인증이 유지된다
'Java & Spring > Spring Security' 카테고리의 다른 글
| 06. 예외 처리 (0) | 2026.08.05 |
|---|---|
| 05. 세션 관리 (0) | 2026.08.05 |
| 03. 인증 아키텍처 (0) | 2026.08.04 |
| 02. 인증 프로세스 (0) | 2026.08.01 |
| 01. 초기화 과정 이해 (0) | 2026.08.01 |