我们使用的架构与this great guide on spring.io中描述的架构非常相似。我们的网关处理身份验证,会话使用spring-session存储在Redis中。我们的微服务终端是安全的,也使用春季会议。
在微服务中,我需要调用另一个微服务的端点。我通过发现客户端轻松获取URL,但我需要提供凭据,而且我不确定实现该目标的最佳方法。
我正在考虑从HttpRequest获取SESSION cookie,将其存储在某种线程局部变量或请求范围bean中,并在RestTemplate中使用它来调用第二个微服务。我需要这个请求范围的bean,因为RestTemplate将用于服务层,即不在MVC控制器中,我不想用我从cookie获得的会话标识符污染我的服务层方法。
有没有更好的方法来满足这种需求? Spring Cloud中是否已经为此提供了一些支持?
非常感谢您的投入
答案 0 :(得分:3)
此时访问Spring Session ID的最简单方法是使用RequestContextHolder.getRequestAttributes().getId()
。一旦您有权访问,您就可以编写自定义ClientHttpRequestInterceptor
以在请求中包含会话ID:
public SpringSessionClientHttpRequestInterceptor implements ClientHttpRequestInterceptor {
public ClientHttpResponse intercept(HttpRequest request, byte[] body, ClientHttpRequestExecution execution)
throws IOException {
boolean isMyService = ...;
// very important not to send the session id to external services
if(isMyService) {
request.getHeaders().add("x-auth-token", RequestContextHolder.getRequestAttributes().getId());
}
}
}
然后,当您创建RestTemplate时,请确保添加SpringSessionClientHttpRequestInterceptor
。
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add(new SpringSessionClientHttpRequestInterceptor());
答案 1 :(得分:0)
我有一个类似的场景,我还需要在RestTemplate
中传递CSRF令牌。我知道您不想在控制器中实现它,但也许它将提供一些其他见解(因为该问题帮助我解决了问题)。这是我在控制器中实现的方式:
@RequestMapping(value = "/assets/download", method = RequestMethod.POST, produces = MediaType.APPLICATION_OCTET_STREAM_VALUE)
public ResponseEntity<Resource> downloadAssets(HttpSession session, @RequestBody SelectedAssets selectedAssets){
...
...
CsrfToken token = (CsrfToken) session.getAttribute("org.springframework.security.web.csrf.HttpSessionCsrfTokenRepository.CSRF_TOKEN");
RestTemplate restTemplate = new RestTemplate();
HttpHeaders headers = new HttpHeaders();
headers.set("Cookie", "SESSION=" + session.getId() + "; XSRF-TOKEN=" + token.getToken());
headers.set("X-XSRF-TOKEN", token.getToken());
HttpEntity<SelectedAssets> selectedAssetsEntity = new HttpEntity<>(selectedAssets, headers);
ResponseEntity<JobResponse> jobResponseResponseEntity = restTemplate.postForEntity("http://localhost:8102/jobs/package", selectedAssetsEntity, JobResponse.class);
...
...
}