Java Spring Boot의 요청 헤더에서 Bearer 토큰을 얻는 방법은 무엇입니까?
안녕하세요 달성하려는 것은 자바 스프링 부트 RESTApi 컨트롤러의 프런트 엔드에서 제출 한 베어러 토큰을 가져오고 다른 마이크로 서비스에 가짜 클라이언트를 사용하여 다른 요청을 수행하는 것입니까? 여기 내가하는 일이야

위의 이미지는 우편 배달부에서 내 요청을 수행하는 방법이며 여기에 내 컨트롤러 코드가 있습니다.
@Operation(summary = "Save new")
@PostMapping("/store")
public ResponseEntity<ResponseRequest<TransDeliveryPlanning>> saveNewTransDeliveryPlanning(
@Valid @RequestBody InputRequest<TransDeliveryPlanningDto> request) {
TransDeliveryPlanning newTransDeliveryPlanning = transDeliveryPlanningService.save(request);
ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();
if (newTransDeliveryPlanning != null) {
response.setMessage(PESAN_SIMPAN_BERHASIL);
response.setData(newTransDeliveryPlanning);
} else {
response.setMessage(PESAN_SIMPAN_GAGAL);
}
return ResponseEntity.ok(response);
}
내 서비스는 다음과 같습니다.
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
String tokenString = "i should get the token from postman, how do i get it to here?";
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData("Bearer " + tokenString, ids);
return () -> partnerDtoResponse;
}
보시다시피, "tokenString"에 제가 질문 한 문자열을 넣었습니다. 우체부에서 어떻게 가져 오나요?
답변
제안 된 답변이 작동하지만 FeignClient
호출 할 때마다 토큰을 전달하는 것이 가장 좋은 방법은 아닙니다. 가짜 요청에 대한 인터셉터를 만들 것을 제안하고 거기에서 토큰을 추출하여 RequestContextHolder
요청 헤더에 직접 추가 할 수 있습니다. 이렇게 :
@Component
public class FeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
public static String getBearerTokenHeader() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization");
}
@Override
public void apply(RequestTemplate requestTemplate) {
requestTemplate.header(AUTHORIZATION_HEADER, getBearerTokenHeader());
}
}
이렇게하면 문제에 대한 깨끗한 솔루션을 얻을 수 있습니다
여기에 몇 가지 옵션이 있습니다.
예를 들어 요청 범위 빈 과 제안한대로 하나의 MVC 인터셉터를 사용할 수 있습니다.
기본적으로 토큰 값에 대한 래퍼를 정의해야합니다.
public class BearerTokenWrapper {
private String token;
// setters and getters
}
그런 다음 MVC 구현을 제공합니다 HandlerInterceptor.
public class BearerTokenInterceptor extends HandlerInterceptorAdapter {
private BearerTokenWrapper tokenWrapper;
public BearerTokenInterceptor(BearerTokenWrapper tokenWrapper) {
this.tokenWrapper = tokenWrapper;
}
@Override
public boolean preHandle(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
final String authorizationHeaderValue = request.getHeader("Authorization");
if (authorizationHeaderValue != null && authorizationHeaderValue.startsWith("Bearer")) {
String token = authorizationHeaderValue.substring(7, authorizationHeaderValue.length());
tokenWrapper.setToken(token);
}
return true;
}
}
이 인터셉터는 MVC 구성에 등록되어야합니다. 예를 들면 :
@EnableWebMvc
@Configuration
public class WebConfiguration extends WebConfigurer { /* or WebMvcConfigurerAdapter for Spring 4 */
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(bearerTokenInterceptor());
}
@Bean
public BearerTokenInterceptor bearerTokenInterceptor() {
return new BearerTokenInterceptor(bearerTokenWrapper());
}
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public BearerTokenWrapper bearerTokenWrapper() {
return new BearerTokenWrapper();
}
}
이 설정으로 Service
해당 빈을 자동 연결하는 데 빈을 사용할 수 있습니다 .
@Autowired
private BearerTokenWrapper tokenWrapper;
//...
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
String tokenString = tokenWrapper.getToken();
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData("Bearer " + tokenString, ids);
return () -> partnerDtoResponse;
}
유사한 솔루션이 여기에 스택 오버플로에서 제공되었습니다. 예를 들어이 관련 질문 을 참조하십시오 .
이 Spring 기반 접근 방식 외에도이 다른 stackoverflow 질문에 노출 된 솔루션과 유사한 것을 시도 할 수 있습니다 .
솔직히 나는 그것을 테스트 한 적이 없지만 Feign 클라이언트 정의에서 바로 요청 헤더 값을 제공 할 수있는 것 같습니다.
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
물론 Controller
다른 Controller
s가 확장 할 수 있는 공통점도 있습니다. 이것은 헤더와 제공된 HTTP 요청 Controller
에서 베어러 토큰을 얻는 데 필요한 로직을 Authorization
제공하지만, 제 생각에는 앞서 언급 한 솔루션 중 어느 것이 든 더 좋습니다.
비슷한 경우가있었습니다. 한 마이크로 서비스의 요청을 가로 채서 토큰을 가져 와서 새 ApiClient를 설정하고이 ApiClient를 사용하여 다른 마이크로 서비스에서 엔드 포인트를 호출했습니다. 하지만 가짜 클라이언트를 미리 구성 할 가능성이 있는지 정말 모르겠습니다. 할 수있는 한 가지는 DefaultApiFilter를 생성하고, 요청을 가로 채고, 데이터베이스에 토큰을 저장 (또는 일부 정적 변수, 일부 싱글 톤 클래스 또는 이와 유사한 것으로 설정) 한 다음이를 사용하려고 할 때 서비스 메서드를 호출하는 것입니다. FeignClient :
package com.north.config;
import org.springframework.stereotype.Component;
import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
@Component
public class DefaultApiFilter implements Filter {
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse,
FilterChain filterChain) throws IOException, ServletException {
HttpServletRequest req = (HttpServletRequest) servletRequest;
String auth = req.getHeader("Authorization");
//TODO if you want you can persist your token here and use it on other place
//TODO This may be used for verification if it comes from the right endpoint and if you should save the token
final String requestURI = ((RequestFacade) servletRequest).getRequestURI();
filterChain.doFilter(servletRequest, servletResponse);
}
}
이 doFilter
메서드는 항상 엔드 포인트가 호출되기 전에 실행되고 나중에 엔드 포인트가 호출됩니다.
나중에를 호출 할 때이를 사용 accountFeignClient.getData("Bearer " + tokenString, ids);
하여 데이터베이스 (또는 보관 한 다른 위치)에서 가져와 여기에서 설정할 수 있습니다.
나는 대답을 얻었지만 더 나은 옵션을 기다릴 것이라고 생각합니다. 여기에 내 대답은 모든 컨트롤러에 @RequestHeader를 추가하여 내 토큰의 가치를 얻고 토큰을 가져와야 String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
하며 여기에 완전한 컨트롤러가 있습니다.
@Operation(summary = "Save new")
@PostMapping("/store")
public ResponseEntity<ResponseRequest<TransDeliveryPlanning>> saveNewTransDeliveryPlanning(@RequestHeader HttpHeaders headers,
@Valid @RequestBody InputRequest<TransDeliveryPlanningDto> request) {
String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
TransDeliveryPlanning newTransDeliveryPlanning = transDeliveryPlanningService.save(token, request);
ResponseRequest<TransDeliveryPlanning> response = new ResponseRequest<TransDeliveryPlanning>();
if (newTransDeliveryPlanning != null) {
response.setMessage(PESAN_SIMPAN_BERHASIL);
response.setData(newTransDeliveryPlanning);
} else {
response.setMessage(PESAN_SIMPAN_GAGAL);
}
return ResponseEntity.ok(response);
}
그리고 나는 어딘가에 무언가를 읽었 Interceptor
으므로 내가 생각하는 모든 컨트롤러에 @RequestHeader를 입력 할 필요가 없지만 그 해결책이나 올바르게 사용하는 방법을 모르겠습니다. 누군가가 더 나은 것을 할 수 있다면 나는 당신의 대답을 받아 들일 것입니다
유틸리티 클래스에서이 간단한 정적 메서드를 만들고이 메서드를 직접 재사용 할 수 있습니다.
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
public class BearerTokenUtil {
public static String getBearerTokenHeader() {
return ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest().getHeader("Authorization");
}
}
귀하의 서비스는 다음과 같습니다.
public TransDeliveryPlanning save(InputRequest<TransDeliveryPlanningDto> request) {
Future<List<PartnerDto>> initPartners = execs.submit(getDataFromAccount(transDeliveryPlanningDtSoDtoPartnerIdsSets));
}
public Callable<List<PartnerDto>> getDataFromAccount(Set<Long> ids) {
List<PartnerDto> partnerDtoResponse = accountFeignClient.getData(BearerTokenUtil.getBearerTokenHeader(), ids);
return () -> partnerDtoResponse;
}
@stacker의 아래 답변이 정확하다고 생각하지만 어떻게 든 불완전하고 "페이 인에서 사용하는 방법"이 누락되었다고 생각합니다.
당신이 가로 챌 수있는 예제를 위해서, 나는 실제 사용 사례를 제공합니다 User-Agent
서비스에 발신자의를하고이 전달 Feign
호출
Feign
주석을 기반으로 클라이언트 를 사용한다고 가정하면 이것이 추가 코드없이 모든 Feign 클라이언트 호출에서 인터셉터를 사용할 수있는 방법입니다.
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
이것은 User-Agent 인터셉터 클래스입니다.
public class UserAgentHeaderInterceptor extends BaseHeaderInterceptor
{
private static final String USER_AGENT = "User-Agent";
public UserAgentHeaderInterceptor()
{
super(USER_AGENT);
}
}
public class BaseHeaderInterceptor implements RequestInterceptor
{
private final String[] headerNames;
public BaseHeaderInterceptor(String... headerNames)
{
this.headerNames = headerNames;
}
@Override
public void apply(RequestTemplate template)
{
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
if (attributes != null)
{
HttpServletRequest httpServletRequest = attributes.getRequest();
for (String headerName : headerNames)
{
String headerValue = httpServletRequest.getHeader(headerName);
if (headerValue != null && !headerValue.isEmpty())
{
template.header(headerName, headerValue);
}
}
}
}
}
귀하의 경우에는이 기본 클래스를 가져 와서 다음과 같은 방식으로 자신 만의 인터셉터를 만들어야합니다. UserAgentHeaderInterceptor