【问题标题】:Propagate HTTP header (JWT Token) over services using spring rest template使用 Spring Rest 模板在服务上传播 HTTP 标头(JWT 令牌)
【发布时间】:2018-03-25 13:09:03
【问题描述】:

我有一个微服务架构,它们都由 Spring Security 和 JWT 令牌保护。

所以,当我调用我的第一个微服务时,我想获取 JWT 令牌并使用这些凭据向另一个服务发送请求。

如何检索令牌并再次发送到其他服务?

【问题讨论】:

  • 有一些代码示例会很棒:)

标签: java spring rest jwt


【解决方案1】:

基本上,您的令牌应该位于请求的标头中,例如: Authorization: Bearer 。要获取它,您可以通过控制器中的 @RequestHeader() 检索任何标头值:

@GetMapping("/someMapping")
public String someMethod(@RequestHeader("Authorization") String token) {

}

现在您可以将令牌放在以下请求的标头中:

HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", token);

HttpEntity<RestRequest> entityReq = new HttpEntity<RestRequest>(request, headers);

现在您可以将 HttpEntity 传递给您的 rest 模板:

template.exchange("RestSvcUrl", HttpMethod.POST, entityReq, SomeResponse.class);

希望我能帮上忙

【讨论】:

  • 完全正确。与任何标题相同。
  • 似乎是相当可靠的解决方案,但它有点“手工”。我正在寻找使用像 ClientHttpRequestInterceptor 这样的拦截器的不同解决方案。但我不知道如何从那里检索令牌。例如,带有 TraceInterceptorConfiguration 的 sleuth 库也在做同样的事情。不需要手动添加标题
  • 或者您可以使用控制器通知(类级别注释)而不是拦截器。在那里您还可以注入标头
【解决方案2】:

我已经完成了任务,创建了一个自定义过滤器

public class RequestFilter implements Filter{



    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        String token = httpServletRequest.getHeader(RequestContext.REQUEST_HEADER_NAME);

        if (token == null || "".equals(token)) {
            throw new IllegalArgumentException("Can't retrieve JWT Token");
        }

        RequestContext.getContext().setToken(token);
        chain.doFilter(request, response);

    }

    @Override
    public void destroy() { }

    @Override
    public void init(FilterConfig arg0) throws ServletException {}


}

然后,在我的配置中设置

    @Bean
public FilterRegistrationBean getPeticionFilter() {

    FilterRegistrationBean registration = new FilterRegistrationBean();
    registration.setFilter(new RequestFilter());
    registration.addUrlPatterns("/*");
    registration.setName("requestFilter");

    return registration;
}

考虑到这一点,我创建了另一个带有 ThreadLocal 变量的类,以将 JWT 令牌从 Controller 传递到 Rest Templace 拦截器

public class RequestContext {

public static final String REQUEST_HEADER_NAME = "Authorization";

private static final ThreadLocal<RequestContext> CONTEXT = new ThreadLocal<>();

private String token;

public static RequestContext getContext() {
    RequestContext result = CONTEXT.get();

    if (result == null) {
        result = new RequestContext();
        CONTEXT.set(result);
    }

    return result;
}

public String getToken() {
    return token;
}

public void setToken(String token) {
    this.token = token;
}

}

public class RestTemplateInterceptor implements ClientHttpRequestInterceptor{

@Override
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution) throws IOException {

    String token = RequestContext.getContext().getToken();

    request.getHeaders().add(RequestContext.REQUEST_HEADER_NAME, token);

    return execution.execute(request, body);

}

}

在配置中添加拦截器

  @PostConstruct
public void addInterceptors() {
    List<ClientHttpRequestInterceptor> interceptors = restTemplate.getInterceptors();
    interceptors.add(new RestTemplateInterceptor());
    restTemplate.setInterceptors(interceptors);
}

【讨论】:

  • 嗨伙计,我也遇到了同样的问题,你能显示所有代码吗?我的 resftul api 工作正常,但客户端有问题。
  • @dragonalvaro 你用过哪个 RequestContext ?
  • 这看起来很危险,因为从未调用过CONTEXT.remove()。 Web 服务器通常使用线程池,如果您将值设置为 threadlocal 并且不删除它,它将在其他请求中可用。 stackoverflow.com/questions/30328437/…
【解决方案3】:

我认为最好将拦截器专门添加到RestTemplate中,如下所示:

class RestTemplateHeaderModifierInterceptor(private val authenticationService: IAuthenticationService) : ClientHttpRequestInterceptor {
    override fun intercept(request: org.springframework.http.HttpRequest, body: ByteArray, execution: ClientHttpRequestExecution): ClientHttpResponse {
        if (!request.headers.containsKey("Authorization")) {
            // don't overwrite, just add if not there.
            val jwt = authenticationService.getCurrentUser()!!.jwt
            request.headers.add("Authorization", "Bearer $jwt")
        }
        val response = execution.execute(request, body)
        return response
    }
}

然后像这样将它添加到 RestTemplate 中:

@Bean
fun restTemplate(): RestTemplate {
    val restTemplate = RestTemplate()
restTemplate.interceptors.add(RestTemplateHeaderModifierInterceptor(authenticationService)) // add interceptor to send JWT along with requests.
    return restTemplate
}

这样,每次您需要 RestTemplate 时,您都可以使用自动装配来获取它。您仍然需要实现 AuthenticationService 才能从 TokenStore 获取令牌,如下所示:


val details = SecurityContextHolder.getContext().authentication.details
if (details is OAuth2AuthenticationDetails) {
   val token = tokenStore.readAccessToken(details.tokenValue)
   return token.value
}

【讨论】:

  • 注意:自 Spring 5.1 起,request.headers.add("Authorization", "Bearer $jwt") 可以替换为 request.headers.setBearerAuth(jwt)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-09-27
  • 2018-07-29
  • 1970-01-01
  • 2018-10-13
  • 2020-07-11
  • 2019-07-25
相关资源
最近更新 更多