使用spring rest模板在服务上传播HTTP头(JWT令牌)

4dbbbstv  于 2022-12-17  发布在  Spring
关注(0)|答案(4)|浏览(142)

我有一个微服务体系结构,它们都由springsecurity和JWT令牌来保护。
因此,当我调用我的第一个微服务时,我希望获得JWT令牌并使用这些凭据向另一个服务发送请求。
如何检索令牌并再次发送到其他服务?

6vl6ewon

6vl6ewon1#

基本上,您的令牌应该位于请求的头部,例如:授权:要获取它,您可以通过控制器中的@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);

希望我能帮忙

cwtwac6a

cwtwac6a2#

我已完成任务,创建了自定义筛选器

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令牌从控制器传递到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);
}
mec1mxoz

mec1mxoz3#

我认为最好将拦截器专门添加到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
}
wf82jlnq

wf82jlnq4#

可能有点晚了,但我认为这是一个常见的问题,关于Web客户端的Spring Security 6.0.0,有一个名为ServletBearerExchangeFilterFunction的类,您可以使用它从安全上下文读取令牌并注入它。

@Bean
public WebClient rest() {
return WebClient.builder()
        .filter(new ServletBearerExchangeFilterFunction())
        .build();

对于RestTemplate,没有自动方式,建议使用过滤器

@Bean
RestTemplate rest() {
  RestTemplate rest = new RestTemplate();
  rest.getInterceptors().add((request, body, execution) -> {
    Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
    if (authentication == null) {
        return execution.execute(request, body);
    }

    if (!(authentication.getCredentials() instanceof AbstractOAuth2Token)) {
        return execution.execute(request, body);
    }

    AbstractOAuth2Token token = (AbstractOAuth2Token) authentication.getCredentials();
    request.getHeaders().setBearerAuth(token.getTokenValue());
    return execution.execute(request, body);
});
return rest;
}

相关问题