OpenFeign 사용 시 헤더 값 넘기기
들어가기 앞서..
- 시큐리티 연동(jwtToken 방식)해둔 상태
api 요청 시 헤더에 토큰을 담겨서 서버로 들어온다. 필터에선 토큰 검증 완료하여 원하는 비즈니스 로직을 처리한다.
비즈니스 로직 중 openfeign으로 외부 API 호출하는 로직이 있으며, 외부 API에게 동일하게 첫 요청 시 전달받았던 토큰을 동일하게 세팅해주어야 했다.
즉, 첫 요청 시 전달받은 헤더값을 openFeign으로 외부API요청 시에도 동일하게 세팅하는 해주어야 했다.
시도 1. 쓰레드로컬변수 사용해볼까?
스프링MVC는 요청마다 쓰레드를 할당받아 사용하니
쓰레드 내 쓰레드로컬변수를 활용하여 토큰을 저장했다가 외부API호출 시 헤더 세팅하고 없애주면 되지않을까??
쓰레드 로컬변수 선언
public class ThreadLocalTokenContext {
public static final ThreadLocal<String> THREAD_LOCAL = new ThreadLocal<>();
}
JwtAuthorizationFilter 내부
@Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
...
final String authorization = request.getHeader(HttpHeaders.AUTHORIZATION);
...
//인증 및 인가 완료 후 비즈니스 로직 내 OpenFeign을 통한 외부API 호출 시 사용을 위해 쓰레드 로컬 변수에 저장
ThreadLocalTokenContext.THREAD_LOCAL.set(authorization);
...
}
OpenFeinClient 내부
1-1. headers 속성 사용
@FeignClient(name="userOpenFeignClient", url="http://localhost:8000")
public interface UserOpenFeignClient {
@GetMapping(value = "/rent/currentRentStatus/user/{userNo}",
headers = "Authroizaiton="+ ThreadLocalTokenContext.THREAD_LOCAL.get()) //headers 속성은 상수여야 한다..
public ApiResponseDto<List<UserRentStatusResDto>> getCurrentRentStatus(@PathVariable Long userNo);
}
문제가 있다....
1. 쓰레드로컬변수 사용 후 삭제해주지 않았다.
2. headers 속성은 상수 값이여야 하나 내가 작성해준 코드는 런타임에 정해지는 값이므로 에러를 뱉는다.
1-2. 내부 메소드의 헤더 세팅
@FeignClient(name="userOpenFeignClient", url="http://localhost:8000")
public interface UserOpenFeignClient {
@GetMapping(value = "/rent/currentRentStatus/user/{userNo}")
public ApiResponseDto<List<UserRentStatusResDto>> getCurrentRentStatus(@RequestHeader(HttpHeaders.AUTHORIZATION) String Authorization, //헤더 세팅
@PathVariable Long userNo);
}
외부API 요청하는 메소드
public List<UserRentStatusResDto> getCurrentRentStatus(Long userNo){
...
ApiResponseDto<List<UserRentStatusResDto>> response = userOpenFeignClient.getCurrentRentStatus(ThreadLocalTokenContext.THREAD_LOCAL.get(), userNo);
ThreadLocalTokenContext.THREAD_LOCAL.remove(); //세팅 완료 후 제거
...
return response.getData();
}
해당 메소드 호출 시 쓰레드로컬변수에 저장된 값을 파라미터로 넘겨주고, 메소드 완료 시 쓰레드 로컬변수 제거하도록 구현했다. 1-1의 문제점인 쓰레드로컬변수 삭제 및 헤더 세팅 부분이 완료되었다. 더하여 openfeign 통한 API호출 시 헤더가 잘 담겨져서 요청되었다.
그러나 이런 식으로 구현할 경우, 외부API 요청 필요로 할때마다 remove()함수가 호출메소드에서 반복될 것이다.
1-3. configuration
@Configuration
@EnableFeignClients(basePackages = "com.example.library")
public class OpenFeignConfig {
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header(HttpHeaders.AUTHORIZATION, ThreadLocalTokenContext.THREAD_LOCAL.get()); //헤더 세팅
ThreadLocalTokenContext.THREAD_LOCAL.remove(); //쓰레드 로컬 제거
};
}
}
RequestInterceptor를 통하여 OpenFeign을 통한 외부 호출 시 헤더 세팅 및 쓰레드로컬 변수 제거를 하였다.
1-2의 문제점이였던 반복적인 remove코드 작성은 사라졌으나 생각해볼 게 있다.
현재 클라이언트 요청 시 openfeign을 통한 외부 호출하는 서비스로직이든 아니든 상관없이 모조리 쓰레드로컬변수에 저장하고 있다. 그러나 제거는 openfeign 통한 외부호출 시에만 삭제가 되고 있다.
즉, 모든 요청에 대해 토큰 저장은 하나 삭제는 openfeign호출 케이스에만 제거된다라는 또 다른 문제점을 지니고 있다.
결론은 쓰레드 반납 시 쓰레드로컬 제거를 해주면 될거 같은데.. 어떻게 해야할까..?
시도 2. HttpserveltRequest 로 되나?
우선 쓰레드로컬을 통하여 해결하지 못하고 configuration 내 HttpserveltRequest를 통하여 헤더 넘기도록 하였다.
remove 로직 도 필요 없었으며 아래 configuration 하나면 해결이 되었다.
헤더 세팅하는 시점이 첫 요청에 대한 같은 스레드를 물고 있기에 가능한 것으로 보인다.
Configuratuion
@Configuration
@EnableFeignClients(basePackages = "com.example.library")
@RequiredArgsConstructor
public class OpenFeignConfig {
private final HttpServletRequest request;
//모든 OpenFeignClient에 대해 헤더 세팅 될건데, 추후 개별 세팅해야할 경우 분리 예정
@Bean
public RequestInterceptor requestInterceptor() {
return requestTemplate -> {
requestTemplate.header(HttpHeaders.AUTHORIZATION, request.getHeader(HttpHeaders.AUTHORIZATION));
};
}
}
Q. 근데 Bean은 부팅될 때 헤더 세팅될텐데 그 시점은 요청이 들어오기도 전이라 토큰도 안받았을텐데 어케 정상 작동하는거지..?
참고
https://groups.google.com/g/ksug/c/Mqv5EkofTaM
spring에서 세션 대신에 특정값들을 저장해놓고 쓸수 있는 방법은 없을까요?
일단 , 질문자님께서 질문 하신 관점에서 보면 권남 님이 말씀하신 HandlerArgumentResolver(Spring 3.1 이상) 혹은 WebArgumentResolver(Spring 3.0 이하) 에 대한 답이 가장 적절해 보입니다.. 저도 짧은 지식으
groups.google.com
SecurityContextHolder 관련 질문이 있습니다! - 인프런
안녕하세요.항상 좋은 강의 감사드립니다!SecurityContextholder 관련 질문이 있어 글을 적게 되었습니다.SecurityContext는 ThreadLocal 기반이고, 이를 전역에서 사용할 수 있도록 Static 변수인 SecurityContextHol
www.inflearn.com