Spring Security / / 2024. 10. 29. 23:53

DelegatingSecurityContextCallable

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 메서드 내에서 CallableDelegatingSecurityContextCallable로 감싸서 사용하여, 자식 스레드에서도 동일한 인증 정보가 유지되도록 합니다.

 

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
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유