Spring Boot의 @Async와 SpringSecurityContext

du·2023년 7월 13일
0

Spring

목록 보기
6/6

로그성 데이터 저장 시 사용할 이벤트핸들러를
@EventListener에 @Async 어노테이션 추가하여 비동기로 구현하려고 하니 수정한사람이 null로 들어가는 문제 발생
(SecurityContext의 인증정보에서 유저이름을 가져와 수정자로 지정하는 구조)

1. 원인

Spring Security Authentication는 ThreadLocal 바인딩 이 Default
@Async를 사용하여 비동기 메서드 작성 시, 로직이 새 스레드에서 실행 되고 SpringSecurityContext의 인증정보에 접근 불가

@RequestMapping(method = RequestMethod.GET, value = "/async")
@ResponseBody
public Object standardProcessing() throws Exception {
    // Before asyncCall: OK
    SecurityContextHolder.getContext()
            .getAuthentication()
            .getPrincipal();

    // Inside Of asyncCall - NPE
    asyncService.asyncCall();

    // After asyncCall: OK
    SecurityContextHolder.getContext()
            .getAuthentication()
            .getPrincipal();

    return SecurityContextHolder.getContext()
            .getAuthentication()
            .getPrincipal();
}

@Async
public void asyncCall() {
    SecurityContextHolder.getContext()
            .getAuthentication()
            .getPrincipal();
}

2. 해결

비동기 스레드 내부에서 SecurityContext 의 인증정보에 액세스하려면,

  1. SpringSecurityHolder 전략을 MODE_INHERITABLETHREADLOCAL로 지정 -> side-effect 때문에 사용 지양!!
  2. DelegatingSecurityContextAsyncTaskExecutor 빈을 생성
@Bean
public DelegatingSecurityContextAsyncTaskExecutor taskExecutor(ThreadPoolTaskExecutor delegate) {
    return new DelegatingSecurityContextAsyncTaskExecutor(delegate);
}

3. 참고문헌

0개의 댓글