DelegatingSecurityContextCallable은 비동기 작업을 수행할 때 **Spring Security의 SecurityContext**를 자식 스레드로 전파하는 데 사용하는 클래스입니다. 보통 SecurityContext는 요청마다 고유하며, 기본적으로는 메인 스레드에서만 유지됩니다. DelegatingSecurityContextCallable을 사용하면 자식 스레드에서도 인증 정보가 동일하게 유지되어, 인증된 사용자 정보가 일관되게 사용됩니다. Spring Security와 함께 비동기 작업을 구성할 때 매우 유용한 클래스입니다.
1. DelegatingSecurityContextCallable이란?
Spring Security에서 SecurityContext는 사용자의 인증 정보와 권한을 저장하며, 기본적으로 각 요청의 메인 스레드에만 한정됩니다. 하지만 비동기 작업이나 멀티스레딩 환경에서는 새로운 스레드가 생성되기 때문에, SecurityContext가 전파되지 않아 인증 정보가 사라지거나 올바르지 않은 상태가 될 수 있습니다.
DelegatingSecurityContextCallable은 메인 스레드의 SecurityContext를 자식 스레드에서도 사용할 수 있도록 복사하여, 비동기 작업에서도 일관된 인증 정보가 유지되도록 합니다.
사용 목적
• 비동기 메서드, 스레드 풀에서 인증 정보 유지가 필요할 때 사용됩니다.
• 비동기 API 호출이나 병렬 처리가 필요한 경우에도 유용합니다.
2. DelegatingSecurityContextCallable 기본 사용 방법
Spring에서 비동기 작업을 수행할 때 ExecutorService와 함께 사용하면 유용합니다. Callable을 스레드 풀에 제출하기 전에, DelegatingSecurityContextCallable로 감싸서 인증 정보를 전파할 수 있습니다.
기본 사용 예시
import org.springframework.security.concurrent.DelegatingSecurityContextCallable;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
public class DelegatingSecurityContextExample {
public static void main(String[] args) throws Exception {
ExecutorService executorService = Executors.newFixedThreadPool(2);
// 기존 SecurityContext 설정
SecurityContext originalContext = SecurityContextHolder.getContext();
originalContext.setAuthentication(...); // 인증 정보 설정
// Callable을 DelegatingSecurityContextCallable로 감싸서 사용
Callable<String> task = () -> {
return "인증된 사용자: " + SecurityContextHolder.getContext().getAuthentication().getName();
};
DelegatingSecurityContextCallable<String> delegatingTask = new DelegatingSecurityContextCallable<>(task);
Future<String> result = executorService.submit(delegatingTask);
System.out.println("비동기 작업 결과: " + result.get());
executorService.shutdown();
}
}
설명
• 기존 SecurityContext 설정: SecurityContextHolder에 인증 정보를 설정합니다.
• DelegatingSecurityContextCallable로 감싸기: Callable을 DelegatingSecurityContextCallable로 감싸서, 비동기 작업에서도 SecurityContext가 유지되도록 합니다.
• ExecutorService 실행: 스레드 풀에 작업을 제출하여 비동기 작업을 수행합니다. 작업 내에서도 SecurityContext를 가져올 수 있습니다.
3. DelegatingSecurityContextCallable의 구조
DelegatingSecurityContextCallable은 내부에서 **메인 스레드의 SecurityContext**를 복사하여 새로운 스레드에서 동일한 인증 정보를 사용하도록 합니다.
주요 메서드
1. createDelegatingContext(): SecurityContext의 사본을 생성하여 자식 스레드에 전달할 수 있도록 합니다.
2. call(): 원래의 Callable이 가진 call() 메서드를 실행하되, 자식 스레드에서도 동일한 SecurityContext가 적용됩니다.
4. DelegatingSecurityContextCallable와 @Async의 사용
Spring의 @Async 애노테이션과 함께 사용할 수도 있습니다. @Async는 자동으로 스레드 풀을 생성하여 비동기 작업을 처리하지만, 기본적으로 SecurityContext가 전파되지 않으므로 DelegatingSecurityContextCallable을 사용해야 합니다.
@Async와 함께 사용하는 예시
import org.springframework.scheduling.annotation.Async;
import org.springframework.security.concurrent.DelegatingSecurityContextCallable;
import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
@Service
public class AsyncService {
private final ExecutorService executorService = Executors.newFixedThreadPool(3);
@Async
public void asyncMethod() {
SecurityContext originalContext = SecurityContextHolder.getContext();
Callable<Void> task = () -> {
System.out.println("인증된 사용자: " + SecurityContextHolder.getContext().getAuthentication().getName());
return null;
};
executorService.submit(new DelegatingSecurityContextCallable<>(task, originalContext));
}
}
설명
• SecurityContext 복사: 메인 스레드의 SecurityContext를 복사하여 DelegatingSecurityContextCallable에 전달합니다.
• @Async 메서드 호출: @Async 메서드 내에서 Callable을 DelegatingSecurityContextCallable로 감싸서 사용하여, 자식 스레드에서도 동일한 인증 정보가 유지되도록 합니다.
5. DelegatingSecurityContextCallable의 장점과 주의사항
장점
• 안전한 인증 정보 전파: 비동기 작업 시 SecurityContext를 안전하게 전파할 수 있습니다.
• 일관된 인증 정보: 자식 스레드에서도 동일한 SecurityContext를 사용할 수 있어, 요청이 다중 스레드에서 실행되더라도 일관된 인증 정보가 유지됩니다.
• Spring Security와 통합: Spring Security의 SecurityContext를 전파하는 표준 방식이므로, 코드의 가독성과 유지보수가 용이합니다.
주의사항
• 성능 이슈: SecurityContext를 매번 복사하여 전달하므로, 대규모 트래픽에서 성능 저하가 발생할 수 있습니다.
• 쓰레드 풀 설정: DelegatingSecurityContextCallable을 사용할 때 스레드 풀 설정을 적절히 조정하여 오버헤드를 최소화해야 합니다.
• 비동기 작업에서만 필요: 단일 스레드에서 실행될 때는 불필요하며, 자식 스레드에서만 SecurityContext 전파가 필요할 때만 사용합니다.
결론
DelegatingSecurityContextCallable은 Spring Security와 멀티스레딩 환경에서 안전하게 인증 정보를 유지할 수 있는 필수적인 클래스입니다. 비동기 작업이나 스레드 풀을 사용하는 경우 SecurityContext가 사라지지 않도록 보장하여, 비즈니스 로직의 일관성과 안정성을 높여줍니다. Spring Security와 함께 DelegatingSecurityContextCallable을 활용하면, 멀티스레딩 환경에서도 안정적으로 인증/인가를 처리할 수 있습니다.
'Spring Security' 카테고리의 다른 글
CSRF(Cross-Site Request Forgery) (2) | 2024.10.30 |
---|---|
@Async (0) | 2024.10.29 |
Callable과 Runnable (0) | 2024.10.29 |
Tomcat Executor (0) | 2024.10.29 |
SecurityContext, SecurityContextHolder, 스레드 처리 방식 (1) | 2024.10.29 |