SecurityConfig: Spring Security 6 호환 필터 등록 순서 수정
Build & Deploy to K8s / build-and-deploy (push) Successful in 1m0s

기존 코드는 addFilterBefore(AiApi, JwtAuthenticationFilter.class) 가
jwt 등록 전에 호출되어 'JwtAuthenticationFilter does not have a
registered order' 예외 발생. Spring Security 6 부터 anchor 는 _이미
등록된_ 필터여야 함.

수정: jwt 를 UsernamePasswordAuthenticationFilter 기준으로 가장 먼저
등록 → JwtAuthenticationFilter.class 가 known map 에 추가됨 →
이후 SubdomainResolver/AiApiKey/TenantConsistency/ForcePw 가 jwt 를
anchor 로 정상 참조.

실행 순서는 동일: SubdomainResolver → AiApiKey → Jwt → TenantConsistency
→ ForcePw → UsernamePasswordAuthenticationFilter

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-29 08:35:08 +09:00
parent 7e3e4078d0
commit 22d073f563
@@ -59,21 +59,26 @@ public class SecurityConfig {
.requestMatchers("/api/**").permitAll()
.anyRequest().authenticated()
)
.addFilterBefore(new AiApiKeyAuthFilter(aiAgentApiKeyService),
JwtAuthenticationFilter.class)
// ⚠️ Spring Security 6 부터 addFilterBefore/After 의 anchor 는 _이미 등록된_
// 필터여야 함. 따라서 JwtAuthenticationFilter 를 가장 먼저 (Spring 표준
// UsernamePasswordAuthenticationFilter 기준으로) 등록한 뒤, 나머지 커스텀
// 필터들이 JwtAuthenticationFilter 를 anchor 로 사용한다.
.addFilterBefore(new JwtAuthenticationFilter(jwtTokenProvider),
UsernamePasswordAuthenticationFilter.class)
// Phase 2 (2026-04-24): 서브도메인 → CompanyResolver → TenantRoutingDataSource 라우팅.
// JwtAuthenticationFilter 보다 앞에서 실행되어야 tenant 컨텍스트가 먼저 결정됨.
.addFilterBefore(
new SubdomainResolverFilter(companyResolver, tenantRoutingDataSource, tenantDbSettings),
JwtAuthenticationFilter.class)
// AiApi 키 인증 — jwt 앞에서 sk-pipe-* 형식 처리, 매칭되지 않으면 jwt 로 통과.
.addFilterBefore(new AiApiKeyAuthFilter(aiAgentApiKeyService),
JwtAuthenticationFilter.class)
// JwtAuthenticationFilter 뒤 — JWT.company_code 와 서브도메인의 company_code 대조.
.addFilterAfter(new TenantConsistencyGuardFilter(jwtTokenProvider),
JwtAuthenticationFilter.class)
// TenantConsistencyGuardFilter 뒤 — 비번 강제 변경 대기자는 허용 경로만 통과.
.addFilterAfter(new ForcePasswordChangeGuardFilter(jwtTokenProvider),
TenantConsistencyGuardFilter.class)
// Phase 2 (2026-04-24): 서브도메인 → CompanyResolver → TenantRoutingDataSource 라우팅.
// JwtAuthenticationFilter 보다 앞에서 실행되어야 tenant 컨텍스트가 먼저 결정됨.
.addFilterBefore(
new SubdomainResolverFilter(companyResolver, tenantRoutingDataSource, tenantDbSettings),
JwtAuthenticationFilter.class);
TenantConsistencyGuardFilter.class);
return http.build();
}