기존 Spring Boot 애플리케이션에 Contexa 통합하기
이미 Spring Security가 적용된 Spring Boot 애플리케이션이 있습니다. Contexa를 추가해도 기존 보안 설정은 대체되지 않습니다 — 그 위에 감싸집니다. 이 페이지는 무엇이 바뀌고, 무엇이 그대로 유지되며, 어떻게 안전하게 도입하는지 설명합니다.
Spring Boot 4.x 미지원 — Contexa는 Spring Boot 3.x(3.0.x ~ 3.4.x)까지만 지원하며, Spring Boot 4.x 환경은 지원하지 않습니다. 4.x 환경에서 @EnableAISecurity가 선언되면 예외(IllegalStateException)와 함께 구동이 즉시 차단되고 로그가 출력됩니다.
내부에서 일어나는 일
Contexa가 인증을 소유하는 CONTEXA_OWNED(@EnableAISecurity(mode = SecurityMode.FULL)의 기본값)에서는, 4계층 파이프라인이 선언적 설정을 Spring Security 필터 체인으로 변환합니다.
기본 SANDBOX 모드(HOST_OWNED) — 속성 없이 선언한 @EnableAISecurity는 SANDBOX 모드이며, contexa.bridge.ownership을 따로 설정하지 않으면 HOST_OWNED로 동작합니다. 이때는 아래의 체인 구성 단계가 실행되지 않습니다. Contexa는 인증 SecurityFilterChain을 만들지 않고, 여러분의 Spring Security 설정이 인증과 인가를 그대로 소유합니다. Contexa의 브리지 필터는 호스트 필터 체인 다음에 실행되어 인증된 요청에서 증거를 읽고, @Protectable 분석은 어노테이션이 붙은 메서드에서 수행됩니다. 자세한 내용은 레거시 시스템 연동을 참고하세요.
Import & Detect
@EnableAISecurity가 AiSecurityImportSelector를 임포트하고, AiSecurityConfiguration을 로드합니다. 커스텀 PlatformConfig 빈이 없으면(@ConditionalOnMissingBean), 기본값이 자동 생성됩니다.
Create Flows
FlowContextFactory가 PlatformConfig를 읽고 각 인증 흐름(Form, REST, MFA, OTT, Passkey)마다 독립적인 HttpSecurity 인스턴스를 생성합니다.
Apply Adapters
SecurityConfigurerOrchestrator가 어댑터 체인을 실행하여 Spring Security 메서드를 호출합니다: http.formLogin(), http.webAuthn()(DSL의 passkey()가 호출), http.oneTimeTokenLogin(). Zero Trust 필터도 이 단계에서 추가됩니다.
Register Chains
SecurityFilterChainRegistrar가 각 HttpSecurity를 OrderedSecurityFilterChain으로 감싸고 BeanDefinitionRegistry를 통해 Spring 빈으로 등록합니다 — 기존 필터 체인과 나란히, 절대 대체하지 않습니다.
기본 PlatformConfig는 Form Login + OTT MFA 흐름, 세션 관리, 그리고 AI 신뢰 점수를 위한 AISessionSecurityContextRepository를 제공합니다 — 설정 클래스를 하나도 작성하지 않아도 됩니다.
마법이 아닙니다. 내부적으로 Contexa는 여러분이 직접 호출하는 것과 동일한 Spring Security 메서드를 사용합니다. 선언적 DSL로 추상화하고 Zero Trust 필터를 추가한 것뿐입니다. 언제든 rawHttp()로 DSL을 우회할 수 있습니다.
기존 보안 설정은 그대로 유지됩니다
새로운 보안 프레임워크를 도입할 때 가장 흔한 우려: "기존 설정이 깨지지 않을까?"
오른쪽의 "별도 추가" 항목은 CONTEXA_OWNED 기준입니다. 기본 SANDBOX 모드(HOST_OWNED)에서는 Contexa가 SecurityFilterChain을 추가하지 않으므로, Contexa 체인에 붙는 Zero Trust 필터와 AI 세션 컨텍스트도 호스트 요청 경로에 추가되지 않습니다.
깨지지 않는 이유
| 기존 빈 | Contexa 추가 후 | 메커니즘 |
|---|---|---|
SecurityFilterChain |
변경 없음 — CONTEXA_OWNED에서는 Contexa 체인이 별도로 추가되고, 기본 HOST_OWNED에서는 Contexa 체인을 만들지 않음 |
BeanDefinitionRegistry + OrderedSecurityFilterChain |
UserDetailsService |
변경 없음 — 기존 구현이 우선 | @ConditionalOnMissingBean |
AuthenticationProvider |
변경 없음 — Contexa가 별도 Provider 추가 | AuthenticationManager 체인 공존 |
| CSRF / CORS 설정 | 변경 없음 — 필요시 global()로 재정의 가능 |
기존 설정 기본 유지 |
spring.security.* |
변경 없음 — 네임스페이스 충돌 없음 | Contexa는 contexa.*와 contexa.security.zerotrust.* 사용 |
Shadow Mode로 안전하게 시작
Shadow Mode는 전체 Zero Trust 파이프라인 — 행동 분석, 위험도 산출, AI 결정 — 을 실행하지만 결정을 절대 강제하지 않습니다.
Shadow Mode
여기서 시작하세요. 기존 동작에 위험 없음.
- AI가 모든 요청을 분석
- 결정은 로그에만 기록
- 사용자 차단이나 MFA 요구 없음
- 실제 트래픽으로 행동 기준선 구축
- 요청 오버헤드 최소 — 분석이 기본적으로 비동기로 수행됨
Enforce Mode
기준선이 확립되면 전환.
- AI가 모든 요청을 분석
- 결정이 실시간으로 강제됨
- ALLOW 정상 통과
- CHALLENGE MFA 요구
- BLOCK 접근 차단
contexa:
security:
zerotrust:
mode: SHADOW # AI가 모든 요청을 분석하되, 강제하지 않음
contexa.security.zerotrust.mode 속성은 SecurityZeroTrustProperties에 바인딩됩니다(값: SHADOW, ENFORCE; 기본값 ENFORCE). SecurityZeroTrustProperties.isEnforcementEnabled()는 mode == ENFORCE일 때만 true를 반환합니다. SHADOW 모드에서는 SecurityDecisionEnforcementHandler가 결정 영속화와 차단 부수 효과를 건너뛰고, AuthorizationManagerMethodInterceptor는 @Protectable(sync = true)의 BLOCK/CHALLENGE/ESCALATE 결정을 관찰 전용으로 처리합니다(ZeroTrustAccessDeniedException을 던지지 않고 메서드가 정상 진행). 이 속성은 @ConfigurationProperties로 바인딩되므로 YAML 값 변경 후에는 애플리케이션 재시작이 필요합니다.
프로덕션에서도 안전합니다. Shadow Mode는 어떤 요청이나 응답도 변경하지 않습니다. AI 분석은 요청 완료 후 비동기로 실행되어 지연 시간이 추가되지 않습니다. Shadow Mode 가이드 →
실전 적용 — Shadow에서 Enforce로
행동 기준선이 확립되면, 속성 하나를 변경하여 Enforce 모드로 전환합니다:
contexa:
security:
zerotrust:
mode: ENFORCE # AI 결정이 실제로 강제됨
Enforce 모드에서 AI의 결정이 실제로 적용됩니다:
롤백하려면 ENFORCE를 SHADOW로 변경하세요. 해당 속성은 @ConfigurationProperties로 바인딩되므로 변경 사항이 적용되려면 애플리케이션 재시작이 필요합니다.
커스텀 설정이 필요할 때
기본 PlatformConfig는 Form Login + OTT MFA + 세션 관리를 포함합니다. 다른 구성이 필요하면 직접 PlatformConfig 빈을 정의하세요(PlatformConfig는 CONTEXA_OWNED에서만 SecurityFilterChain으로 구성되며, HOST_OWNED에서는 여러분의 Spring Security 설정이 그대로 사용됩니다):
시나리오 A: REST API 전용
애플리케이션이 REST API만 제공하고 Form Login이 필요 없는 경우.
@Bean
public PlatformConfig platformConfig(IdentityDslRegistry<HttpSecurity> registry) throws Exception {
return registry
.global(globalHttpCustomizer)
.rest(rest -> rest.order(10))
.session(Customizer.withDefaults())
.build();
}
시나리오 B: 기존 Form Login + Zero Trust
기존 인증 흐름을 유지하면서 AI 기반 보안만 추가하는 경우. 핵심: AISessionSecurityContextRepository 등록 필수.
@Bean
public PlatformConfig platformConfig(IdentityDslRegistry<HttpSecurity> registry) throws Exception {
return registry
.global(http -> http
.authorizeHttpRequests(authReq -> authReq
.requestMatchers("/css/**", "/js/**", "/images/**").permitAll()
.anyRequest().access(customDynamicAuthorizationManager))
.securityContext(sc -> sc
.securityContextRepository(aiSessionSecurityContextRepository)))
.form(form -> form.defaultSuccessUrl("/dashboard"))
.session(Customizer.withDefaults())
.build();
}
시나리오 C: 복잡한 기존 설정 — rawHttp() 탈출구
DSL이 충분하지 않을 때 — 커스텀 필터, 커스텀 AuthenticationEntryPoint, 고급 설정 — Spring Security의 HttpSecurity에 직접 접근합니다:
.form(form -> form
.rawFormLogin(formLogin -> formLogin
.authenticationDetailsSource(customDetailsSource)
.successHandler(customSuccessHandler))
)
Spring Security의 FormLoginConfigurer에 완전히 접근하면서도 Contexa의 Zero Trust 파이프라인은 그대로 유지됩니다.
Zero Trust 필수 조건. 커스텀 PlatformConfig를 정의할 때 AISessionSecurityContextRepository를 global() 커스터마이저에 반드시 등록하세요. 빠뜨리면 세션에 AI 신뢰 점수가 주입되지 않아 Zero Trust가 작동하지 않습니다.