Comment obtenir un jeton de porteur à partir de l'en-tête d'une requête dans Java Spring Boot?
Salut, ce que vous essayez de réaliser est d'obtenir le jeton de porteur qui a été soumis à partir du front-end dans le contrôleur java spring boot RESTApi et de faire une autre demande en utilisant un client feint à un autre microservices? voici ce que je fais

l'image ci-dessus montre comment je fais ma demande du facteur, et voici mon code de contrôleur:
@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);
}
et voici à quoi ressemble mon service:
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;
}
comme vous pouvez le voir, dans "tokenString", j'ai mis une chaîne sur laquelle je me suis interrogé, comment puis-je l'obtenir à partir du facteur?
Réponses
Bien que les réponses suggérées fonctionnent, transmettre le jeton à chaque fois aux FeignClient
appels n'est toujours pas la meilleure façon de le faire. Je suggérerais de créer un intercepteur pour les demandes de simulation et là, vous pouvez extraire le jeton RequestContextHolder
et l'ajouter directement à l'en-tête de la demande. comme ça:
@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 cette façon, vous avez une solution propre à votre problème
Vous avez plusieurs options ici.
Par exemple, vous pouvez utiliser un bean à portée de requête et, comme vous le suggérez, un intercepteur MVC .
Fondamentalement, vous devez définir un wrapper pour la valeur du jeton:
public class BearerTokenWrapper {
private String token;
// setters and getters
}
Ensuite, fournissez une implémentation d'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;
}
}
Cet intercepteur doit être enregistré dans votre configuration MVC. Par exemple:
@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();
}
}
Avec cette configuration, vous pouvez utiliser le bean dans votre Service
autowiring le bean correspondant:
@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;
}
Des solutions similaires ont été fournies ici dans le débordement de pile. Voir, par exemple, cette question connexe .
En plus de cette approche basée sur Spring, vous pouvez essayer quelque chose de similaire à la solution exposée dans cette autre question de stackoverflow .
Honnêtement, je ne l'ai jamais testé, mais il semble que vous puissiez fournir la valeur de l'en-tête de la demande directement dans la définition du client Feign, dans votre cas, quelque chose comme:
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
Bien sûr, vous pouvez également une commune Controller
que d'autres Controller
peuvent étendre. Cela Controller
fournira la logique nécessaire pour obtenir le jeton de support à partir de l'en- Authorization
tête et de la requête HTTP fournie, mais à mon avis, toutes les solutions susmentionnées sont meilleures.
J'ai eu un cas similaire. J'interceptais les demandes d'un microservice, obtenais le jeton et le définissais mon nouveau ApiClient et appelais le point de terminaison d'un autre microservice utilisant cet ApiClient. Mais je ne sais vraiment pas s'il est possible de préconfigurer le client feign. Une chose que vous pouvez faire est de créer DefaultApiFilter, d'intercepter la demande, d'enregistrer le jeton dans votre base de données (ou de le définir sur une variable statique, une classe singleton ou quelque chose de similaire), puis appeler votre méthode de service lorsque vous essayez d'utiliser le 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);
}
}
Cette doFilter
méthode sera toujours exécutée avant qu'un point de terminaison ne soit appelé, et plus tard, le point de terminaison sera appelé.
Et plus tard, utilisez-le lorsque vous appelez le, accountFeignClient.getData("Bearer " + tokenString, ids);
vous pouvez l'obtenir à partir de votre base de données (ou de tout autre endroit où vous l'avez conservé) et le définir ici.
J'ai eu la réponse mais je pense que j'attendrai toujours une meilleure option, car ma réponse ici est que je dois ajouter @RequestHeader dans chaque contrôleur pour obtenir la valeur de mon jeton et obtenir le jeton avec String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
, et voici mon contrôleur complet:
@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);
}
et j'ai lu quelque part il y a quelque chose qui s'appelle Interceptor
donc nous n'avons pas à taper @RequestHeader dans chaque contrôleur je pense, mais je ne sais pas si c'est la solution ou comment l'utiliser correctement. si quelqu'un peut faire cela avec quelque chose de mieux, j'accepterai la vôtre comme réponse
Vous pouvez créer cette méthode statique simple dans une classe d'utilitaire et réutiliser cette méthode directement.
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");
}
}
Votre service ressemblerait alors à ceci
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;
}
Je pense que la réponse ci-dessous de @stacker est correcte, mais je pense aussi qu'elle est en quelque sorte incomplète et manque le "comment l'utiliser dans Feign".
Pour les besoins de l'exemple, je vais fournir un cas d'utilisation réel où vous pouvez intercepter User-Agent
l'appelant vers votre service et le transférer dans l' Feign
appel
En supposant que vous utilisez des Feign
clients basés sur des annotations, voici comment vous pouvez utiliser l'intercepteur dans tous vos appels clients Feign sans code supplémentaire
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
C'est la classe d'intercepteur 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);
}
}
}
}
}
Dans votre cas, il vous suffit de prendre cette classe de base et de créer votre propre intercepteur de la même manière que le UserAgentHeaderInterceptor