Jak uzyskać token okaziciela z nagłówka żądania w rozruchu java Spring?
Cześć, co próbujesz osiągnąć, to uzyskać token okaziciela, który został przesłany z interfejsu użytkownika w kontrolerze RESTApi rozruchu java spring boot i wykonać kolejne żądanie za pomocą klienta fałszywego do innej mikrousługi? oto co robię

obrazek powyżej pokazuje, jak mogę wykonać moją prośbę od listonosza, a oto mój kod kontrolera:
@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);
}
a tak wygląda moja usługa:
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;
}
jak widzisz, w "tokenString" umieściłem ciąg, o który pytałem, jak mogę go tam dostać od listonosza?
Odpowiedzi
Chociaż sugerowane odpowiedzi działają, przekazywanie tokena za każdym razem do FeignClient
połączeń nadal nie jest najlepszym sposobem na zrobienie tego. Sugerowałbym utworzenie przechwytywacza dla żądań pozorowanych i tam można wyodrębnić token z RequestContextHolder
i dodać go bezpośrednio do nagłówka żądania. lubię to:
@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());
}
}
w ten sposób masz czyste rozwiązanie swojego problemu
Masz tutaj kilka opcji.
Na przykład można użyć komponentu bean o zasięgu żądania i, jak sugerujesz, jednego przechwytywacza MVC .
Zasadniczo musisz zdefiniować opakowanie dla wartości tokenu:
public class BearerTokenWrapper {
private String token;
// setters and getters
}
Następnie zapewnij implementację 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;
}
}
Ten przechwytywacz powinien być zarejestrowany w konfiguracji MVC. Na przykład:
@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();
}
}
Dzięki tej konfiguracji możesz użyć ziarna do automatycznego Service
podłączania odpowiedniego ziarna:
@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;
}
Podobne rozwiązania zastosowano tutaj w przypadku przepełnienia stosu. Zobacz na przykład to powiązane pytanie .
Oprócz tego podejścia opartego na Spring, możesz spróbować czegoś podobnego do rozwiązania przedstawionego w tym innym pytaniu o przepełnienie stosu .
Szczerze mówiąc, nigdy tego nie testowałem, ale wydaje się, że możesz podać wartość nagłówka żądania bezpośrednio w definicji klienta Feign, w twoim przypadku coś takiego:
@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/data")
List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}
Oczywiście, można również częstym Controller
że inne Controller
s może przedłużyć. To Controller
zapewni logikę niezbędną do uzyskania okaziciela Reklamowe z Authorization
nagłówkiem i żądania HTTP dostarczonych, ale moim zdaniem każdy z wyżej wymienionych rozwiązań są lepsze.
Miałem podobny przypadek. Przechwytywałem żądania z jednej mikrousługi, pobierałem token i ustawiałem go jako mój nowy ApiClient i wywoływałem punkt końcowy z innej mikrousługi przy użyciu tego ApiClient. Ale naprawdę nie wiem, czy istnieje możliwość wstępnej konfiguracji pozorowanego klienta. Jedną z rzeczy, które możesz zrobić, to utworzyć DefaultApiFilter, przechwycić żądanie, zapisać token w swojej bazie danych (lub ustawić go na jakąś zmienną statyczną, jakąś klasę pojedynczą lub coś podobnego), a następnie wywołać w niej swoją metodę usługi podczas próby użycia 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);
}
}
Ta doFilter
metoda będzie zawsze wykonywana przed wywołaniem dowolnego punktu końcowego, a później zostanie wywołany punkt końcowy.
A później użyj go, dzwoniąc accountFeignClient.getData("Bearer " + tokenString, ids);
, możesz go pobrać ze swojej bazy danych (lub z dowolnego innego miejsca, w którym ją przechowujesz) i ustawić tutaj.
Mam odpowiedź, ale myślę, że nadal będę czekać na lepszą opcję, ponieważ moja odpowiedź brzmi: muszę dodać @RequestHeader do każdego kontrolera, aby uzyskać wartość mojego tokena i uzyskać token String token = headers.getFirst(HttpHeaders.AUTHORIZATION);
, a oto mój kompletny kontroler:
@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);
}
i czytałem gdzieś, że jest coś Interceptor
takiego, że nie musimy wpisywać @RequestHeader w każdym kontrolerze, o którym myślę, ale nie wiem, czy to rozwiązanie i jak go poprawnie używać. jeśli ktoś może to zrobić za pomocą czegoś lepszego, przyjmuję twoją odpowiedź
Możesz utworzyć tę prostą statyczną metodę w klasie narzędziowej i bezpośrednio użyć tej metody ponownie.
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");
}
}
Twoja usługa będzie wtedy wyglądać tak
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;
}
Myślę, że poniższa odpowiedź z @stacker jest poprawna, ale myślę, że jest w jakiś sposób niekompletna i brakuje jej „jak go używać w Feign”.
Dla przykładu przedstawię prawdziwy przypadek użycia, w którym możesz przechwycić User-Agent
dzwoniącego do swojej usługi i przekazać to w Feign
połączeniu
Zakładając, że używasz Feign
klientów na podstawie adnotacji, w ten sposób możesz używać przechwytywacza we wszystkich połączeniach klientów Feign bez dodatkowego kodu
@Configuration
@EnableFeignClients(
defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
@Bean
public RequestInterceptor userAgentHeaderInterceptor() {
return UserAgentHeaderInterceptor();
}
}
To jest klasa przechwytująca 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);
}
}
}
}
}
W twoim przypadku wystarczy wziąć tę klasę bazową i utworzyć własny przechwytywacz w taki sam sposób jak UserAgentHeaderInterceptor