繁体   English   中英

如何从 java spring 启动中的请求的 header 获取不记名令牌?

[英]How to get bearer token from header of a request in java spring boot?

嗨,试图实现的是获取从 java spring 引导 RESTApi controllers 的前端提交的不记名令牌并使用另一个客户端请求另一个请求? 这就是我所做的

在此处输入图像描述

上图是我如何从 postman 提出请求,这是我的 controller 代码:

@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 到那里?

尽管建议的答案有效,但每次将令牌传递给FeignClient调用仍然不是最好的方法。 我建议为假装请求创建一个拦截器,在那里您可以从RequestContextHolder中提取令牌并将其添加到直接请求 header 中。 像这样:

    @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());
       
      }
    }

这样你就有一个干净的解决方案来解决你的问题

您在这里有几个选择。

例如,您可以使用请求范围的 bean ,并按照您的建议,使用一个MVC 拦截器

基本上,您需要为令牌值定义一个包装器:

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 自动装配相应的 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 question中公开的解决方案类似的方法。

老实说,我从未测试过它,但似乎您可以在 Feign 客户端定义中提供请求 header 值,在您的情况下类似于:

@FeignClient(name="AccountFeignClient")
public interface AccountFeignClient {    
    @RequestMapping(method = RequestMethod.GET, value = "/data")
    List<PartnerDto> getData(@RequestHeader("Authorization") String token, Set<Long> ids);
}

当然,也可以是其他Controller可以扩展的普通Controller 这个Controller将提供从Authorization header 和 HTTP 请求获取不记名令牌所需的逻辑,但我认为上述任何解决方案都更好。

我有一个类似的案例。 我正在拦截来自一个微服务的请求,获取令牌并将其设置为我的新 ApiClient 并使用此 ApiClient 从另一个微服务调用端点。 但我真的不知道是否有可能预先配置 feign 客户端。 您可以做的一件事是创建 DefaultApiFilter,拦截请求,将令牌保存在数据库中(或将其设置为一些 static 变量,一些 singleton ZA2F2ED4F8EBC2CBB4C21A29DC40AB61Z 当尝试使用类似的东西或调用您的服务方法时)假装客户端:

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); 您可以从您的数据库(或从您保存它的任何其他地方)获取它并在此处设置。

从 header 获取不记名令牌的一种简单方法是将@RequestHeader与 header 名称一起使用。

请参阅下面的代码示例

@PostMapping("/some-endpoint")
public ResponseEntity<String> someClassNmae(@RequestHeader("Authorization") String bearerToken) {

   System.out.println(bearerToken); // print out bearer token

   // some more code
}

我得到了答案,但我认为我仍然会等待更好的选择,因为我的答案是我必须在每个 controller 添加 @RequestHeader 以获取我的令牌的值并使用String token = headers.getFirst(HttpHeaders.AUTHORIZATION); ,这是我完整的 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);
}

我在某处读到有一种叫做Interceptor的东西,所以我们不必在我认为的每个 controller 中输入@RequestHeader,但我不知道该解决方案是否或如何正确使用它。 如果有人可以用更好的方法做到这一点,我会接受你的回答

我认为@stacker 下面的答案是正确的,但我认为在某种程度上是不完整的,并且缺少“如何在 Feign 中使用它”。

为了示例,我将提供一个真实的用例,您可以在其中拦截调用者的User-Agent到您的服务并在Feign调用中转发它

假设您使用基于注释的Feign客户端,您可以在所有 Feign 客户端调用中使用拦截器,而无需任何额外代码

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

这是用户代理拦截器 class

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

在您的情况下,您只需要使用此基础 class 并以与UserAgentHeaderInterceptor相同的方式创建自己的拦截器

使用这个注解获取前端返回的header信息: @RequestHeader("Authorization") String token

例子:

@GetMapping("/hello")
    public void hello(@RequestHeader("Authorization") String token){

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM