¿Cómo obtener el token de portador del encabezado de una solicitud en java spring boot?
Hola, lo que intento lograr es obtener el token de portador que se envió desde el front-end en el controlador RESTApi de arranque de java spring y hacer otra solicitud usando un cliente fingido a otros microservicios. esto es lo que hago
La imagen de arriba es cómo hago mi solicitud del cartero, y aquí está mi código de controlador:
@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);
}
y así es como se ve mi servicio:
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;
}
como puede ver, en "tokenString" allí puse una cadena sobre la que pregunté, ¿cómo la consigo del cartero?
Respuestas
Aunque las respuestas sugeridas funcionan, pasar el token cada vez a las FeignClientllamadas todavía no es la mejor manera de hacerlo. Sugeriría crear un interceptor para solicitudes fingidas y allí puede extraer el token RequestContextHoldery agregarlo al encabezado de solicitud directamente. Me gusta esto:
@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());
}
}
de esta manera tienes una solución limpia para tu problema
Tienes varias opciones aquí.
Por ejemplo, puede utilizar un bean con ámbito de solicitud y, como sugiere, un interceptor MVC .
Básicamente, debe definir un contenedor para el valor del token:
public class BearerTokenWrapper {
private String token;
// setters and getters
}
Luego, proporcione una implementación de un 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;
}
}
Este interceptor debe registrarse en su configuración MVC. Por ejemplo:
@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();
}
}
Con esta configuración, puede usar el bean en su Servicecableado automático del bean correspondiente:
@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;
}
Se han proporcionado soluciones similares aquí en el desbordamiento de pila. Vea, por ejemplo, esta pregunta relacionada .
Además de este enfoque basado en Spring, puede probar algo similar a la solución expuesta en esta otra pregunta de stackoverflow .
Honestamente, nunca lo he probado, pero parece que puede proporcionar el valor del encabezado de la solicitud directamente en la definición del cliente de Feign, en su caso, algo como:
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
Por supuesto, también puede un común Controllerque otros Controllers pueden extender. Esto Controllerproporcionará la lógica necesaria para obtener el token de portador del Authorizationencabezado y la solicitud HTTP proporcionada, pero en mi opinión, cualquiera de las soluciones mencionadas anteriormente es mejor.
Tuve un caso similar. Estaba interceptando las solicitudes de un microservicio, obteniendo el token y configurándolo en mi nuevo ApiClient y llamando al punto final desde otro microservicio usando este ApiClient. Pero realmente no sé si existe la posibilidad de preconfigurar el cliente fingido. Una cosa que puede hacer es crear DefaultApiFilter, interceptar la solicitud, guardar el token en su base de datos (o configurarlo en alguna variable estática, alguna clase singleton o algo similar) y luego llamar a su método de servicio cuando intente usar el 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);
}
}
Este doFiltermétodo siempre se ejecutará antes de que se llame a cualquier punto final, y luego se llamará al punto final.
Y luego úselo cuando llame al accountFeignClient.getData("Bearer " + tokenString, ids);, puede obtenerlo de su base de datos (o de cualquier otro lugar donde lo guardó) y configurarlo aquí.
obtuve la respuesta, pero creo que todavía esperaré una mejor opción, ya que mi respuesta aquí es que tengo que agregar @RequestHeader en cada controlador para obtener el valor de mi token y obtener el token String token = headers.getFirst(HttpHeaders.AUTHORIZATION);, y aquí está mi controlador completo:
@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);
}
y leí en algún lugar que hay algo llamado, Interceptorpor lo que no tenemos que escribir @RequestHeader en cada controlador, creo, pero no sé si esa es la solución o cómo usarla correctamente. si alguien puede hacer esto con algo mejor, aceptaré la tuya como respuesta
Puede crear este método estático simple en una clase de utilidad y reutilizar este método directamente.
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");
}
}
Su servicio se vería así
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;
}
Creo que la respuesta a continuación de @stacker es correcta, pero también creo que de alguna manera está incompleta y le falta "cómo usarlo en Fingir".
Por el bien del ejemplo, proporcionaré un caso de uso real en el que puede interceptar el User-Agentde la persona que llama a su servicio y reenviarlo en la Feignllamada.
Suponiendo que usa Feignclientes basados en anotaciones, así es como puede usar el interceptor en todas las llamadas de sus clientes Feign sin ningún código adicional
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
Esta es la clase de interceptor de agente de usuario
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);
}
}
}
}
}
En su caso, solo necesita tomar esta clase base y crear su propio interceptor de la misma manera que el UserAgentHeaderInterceptor