Wie bekomme ich einen Inhaber-Token aus dem Header einer Anfrage in Java Spring Boot?
Hallo, was Sie erreichen möchten, ist, ein Inhaber-Token zu erhalten, das vom Front-End im Java Spring Boot RESTApi-Controller gesendet wurde, und eine weitere Anfrage mit dem Feign-Client an einen anderen Microservices zu senden. Hier ist was ich tue

Bild oben ist, wie ich meine Anfrage vom Postboten mache, und hier ist mein Controller-Code:
@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);
}
und so sieht mein Service aus:
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;
}
Wie Sie sehen können, habe ich in "tokenString" dort eine Zeichenfolge eingefügt, über die ich Fragen gestellt habe. Wie komme ich vom Postboten dorthin?
Antworten
Obwohl die vorgeschlagenen Antworten funktionieren, ist es FeignClient
immer noch nicht der beste Weg , das Token jedes Mal an Anrufe zu übergeben. Ich würde vorschlagen, einen Interceptor für vorgetäuschte Anforderungen zu erstellen. Dort können Sie das Token extrahieren RequestContextHolder
und direkt zum Anforderungsheader hinzufügen. so was:
@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());
}
}
Auf diese Weise haben Sie eine saubere Lösung für Ihr Problem
Sie haben hier mehrere Möglichkeiten.
Sie können beispielsweise eine Bean mit Anforderungsbereich und, wie Sie vorschlagen, einen MVC-Interceptor verwenden .
Grundsätzlich müssen Sie einen Wrapper für den Token-Wert definieren:
public class BearerTokenWrapper {
private String token;
// setters and getters
}
Stellen Sie dann eine Implementierung einer MVC bereit 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;
}
}
Dieser Interceptor sollte in Ihrer MVC-Konfiguration registriert sein. Zum Beispiel:
@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();
}
}
Mit diesem Setup können Sie die Bean in Ihrem Service
Autowire für die entsprechende Bean verwenden:
@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;
}
Ähnliche Lösungen wurden hier im Stapelüberlauf bereitgestellt. Siehe zum Beispiel diese verwandte Frage .
Zusätzlich zu diesem Spring-basierten Ansatz können Sie etwas Ähnliches wie die in dieser anderen Frage zum Stapelüberlauf dargestellte Lösung ausprobieren .
Ehrlich gesagt habe ich es noch nie getestet, aber es scheint, dass Sie den Wert des Anforderungsheaders direkt in der Feign-Client-Definition angeben können, in Ihrem Fall wie folgt:
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
Natürlich können Sie auch eine gemeinsame Controller
, die andere Controller
s erweitern können. Dies Controller
liefert die Logik, die erforderlich ist, um das Inhaber-Token aus dem Authorization
Header und der bereitgestellten HTTP-Anforderung zu erhalten, aber meiner Meinung nach ist jede der oben genannten Lösungen besser.
Ich hatte einen ähnlichen Fall. Ich habe die Anforderungen von einem Microservice abgefangen, das Token abgerufen, meinen neuen ApiClient festgelegt und den Endpunkt von einem anderen Microservice mit diesem ApiClient aufgerufen. Aber ich weiß wirklich nicht, ob es eine Möglichkeit gibt, einen falschen Client vorkonfigurieren zu können. Sie können DefaultApiFilter erstellen, die Anforderung abfangen, das Token in Ihrer Datenbank speichern (oder auf eine statische Variable, eine Singleton-Klasse oder ähnliches setzen) und dann Ihre Dienstmethode aufrufen, wenn Sie versuchen, das zu verwenden 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);
}
}
Diese doFilter
Methode wird immer ausgeführt, bevor ein Endpunkt aufgerufen wird, und später wird der Endpunkt aufgerufen.
Und später beim Aufrufen des accountFeignClient.getData("Bearer " + tokenString, ids);
können Sie es aus Ihrer Datenbank (oder von einem anderen Ort, an dem Sie es aufbewahrt haben) abrufen und hier einstellen.
Ich habe die Antwort bekommen, aber ich denke, dort werde ich immer noch auf eine bessere Option warten, da meine Antwort hier ist, dass ich @RequestHeader in jeden Controller einfügen muss, um den Wert meines Tokens zu erhalten und das Token mit zu erhalten String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
, und hier ist mein vollständiger Controller:
@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);
}
und ich habe irgendwo gelesen, dass es etwas gibt, das heißt, Interceptor
also müssen wir nicht @RequestHeader in jeden Controller eingeben, den ich denke, aber ich weiß nicht, ob das die Lösung ist oder wie man es richtig verwendet. Wenn jemand dies mit etwas Besserem tun kann, werde ich Ihre als Antwort akzeptieren
Sie können diese einfache statische Methode in einer Dienstprogrammklasse erstellen und diese Methode direkt wiederverwenden.
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");
}
}
Ihr Service würde dann so aussehen
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;
}
Ich denke, die Antwort von @stacker unten ist richtig, aber ich denke auch, dass sie irgendwie unvollständig ist und das "Wie man es in Feign verwendet" fehlt.
Aus Gründen der diesem Beispiel werde ich einen realen Anwendungsfall an , wo Sie die abfangen User-Agent
des Anrufers zu Ihren Diensten und leitet diese in den Feign
Aufruf
Angenommen, Sie verwenden Feign
Clients basierend auf Anmerkungen. Auf diese Weise können Sie den Interceptor in all Ihren Feign-Clientaufrufen ohne zusätzlichen Code verwenden
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
Dies ist die User-Agent-Interceptor-Klasse
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);
}
}
}
}
}
In Ihrem Fall müssen Sie entweder diese Basisklasse nehmen und Ihren eigenen Interceptor auf die gleiche Weise wie den erstellen UserAgentHeaderInterceptor