如何在JavaSpringBoot中从请求头中获取承载令牌?

ogsagwnx  于 2021-07-06  发布在  Java
关注(0)|答案(6)|浏览(581)

嗨,尝试实现的是在JavaSpringBootRestaPI控制器中获取从前端提交的承载令牌,并使用假客户机向另一个微服务执行另一个请求?我就是这么做的

上图是我如何向 Postman 请求的,下面是我的控制器代码:

@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”中,我放了一个我质疑的字符串,如何从 Postman 那里得到它?

agxfikkp

agxfikkp1#

我得到了答案,但我认为我仍将等待更好的选择,因为我的答案是我必须在每个控制器中添加@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,但我不知道这是否是解决方案,也不知道如何正确使用它。如果有人能做得更好,我会接受你的回答

zpf6vheq

zpf6vheq2#

我也遇到过类似的情况。我拦截来自一个微服务的请求,获取令牌并将其设置为我的新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); 您可以从您的数据库(或从您保存它的任何其他地方)获取它并将其设置在此处。

dm7nw8vv

dm7nw8vv3#

你有几个选择。
例如,您可以使用一个请求范围的bean和一个mvc拦截器。
基本上,您需要为令牌值定义一个 Package 器:

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 自动关联相应的bean:

@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问题中公开的解决方案的方法。
老实说,我从未对其进行过测试,但您似乎可以在外部客户机定义中提供请求头值,例如:

@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可以延伸。这个 Controller 将提供从服务器获取承载令牌所需的逻辑 Authorization 头和http请求,但在我看来上述任何解决方案都更好。

ki1q1bka

ki1q1bka4#

尽管建议的答案是有效的,但每次都会将令牌传递给 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());

      }
    }

这样你就有了一个干净的解决方案

am46iovg

am46iovg5#

我认为下面@stacker给出的答案是正确的,但我也认为不完整,缺少了“如何在外国使用它”。
出于示例的考虑,我将提供一个实际的用例,您可以在其中拦截 User-Agent 并在 Feign 呼叫
假设你使用 Feign 基于注解的客户机这就是如何在所有外部客户机调用中使用拦截器,而无需任何额外代码

@Configuration
@EnableFeignClients(
    defaultConfiguration = DefaultFeignConfiguration.class
)
public class FeignConfig
{
}
@Configuration
@Import(FeignClientsConfiguration.class)
public class DefaultFeignConfiguration
{
    @Bean
    public RequestInterceptor userAgentHeaderInterceptor() {
        return UserAgentHeaderInterceptor();
    } 
}

这是用户代理拦截器类

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

b09cbbtk

b09cbbtk6#

您可以在实用程序类中创建这个简单的静态方法,并直接重用这个方法。

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;
}

相关问题