问题
We are using an architecture very similar to the one described in this great guide on spring.io. Our gateway handles authentication, and sessions are stored in Redis using spring-session. Endpoints of our micro services are secured and also use spring-session.
In a micro service I need to call an endpoint of another micro service. I get the URL easily through the discovery client, but I need to provide credentials and I'm not sure of the best way to achieve that.
I am thinking about getting the SESSION cookie from the HttpRequest, store it in some kind of thread local variables or request scope bean, and use it in the RestTemplate to call the second micro service. I need this request scoped bean because the RestTemplate will be used in the service layer, i.e. not in the MVC controller, and I don't want to pollute my service layer methods with this session identifier I get from the cookie.
Is there a better way to approach this need? Is there already some support in Spring Cloud for this?
Thanks a lot for your input
回答1:
At this time the easiest way to access the Spring Session id is using RequestContextHolder.getRequestAttributes().getId()
. Once you have access to that you can write a custom ClientHttpRequestInterceptor
to include the session id in requests:
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());
}
}
}
Then when you create your RestTemplate make sure to add SpringSessionClientHttpRequestInterceptor
.
RestTemplate rest = new RestTemplate();
rest.getInterceptors().add(new SpringSessionClientHttpRequestInterceptor());
回答2:
I had a similar scenario where I also needed to pass the CSRF token in the RestTemplate
. I know you don't want to implement it in the controller but maybe it will give some additional insight (as this question helped me solve the issue). Here is how I implemented it in my controller:
@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);
...
...
}
来源:https://stackoverflow.com/questions/31875205/best-way-to-propagate-credentials-between-micro-services-using-spring-session