问题
I would like to know if someone has an example to see how to implement "Token Exchange" technique with Spring Cloud Security (with OAuth2).
Currently I have implemented "Token Relay" technique in a Microservices Environment using ZuulProxy to "relay" the OAuth2 token and implementing SSO. This is great but implies that every microservice uses the same clientId (which is specified in ZuulProxy setup as ZuulProxy relays the token only with authorization_code grant type and the clientId provided). However, for intra-microservices calls I would like to "exchange" the token. This means in some cases the token that ZuulProxy relays is not the one I need to use to authenticate/authorize Microservice A as client of Microservice B.
The Spring Cloud reference documentation currently says: "Building on Spring Boot and Spring Security OAuth2 we can quickly create systems that implement common patterns like single sign on, token relay and token exchange." (http://cloud.spring.io/spring-cloud-security/spring-cloud-security.html)
I guess that with "Token Exchange" in the reference documentation they mean the implementation of this extension of OAuth2, explained in this spec, which is basically what I need: https://tools.ietf.org/html/draft-ietf-oauth-token-exchange-03
As I said, I understand how to use SSO and Token Relay but I'm not able to see further explanation about how to implement "Token exchange" in the reference documentation. I was not able to find an implementation example either.
Does anyone know where I can find further information or an example?
Thanks so much!
回答1:
I'm curious why you'd need to "exchange" the token for making calls from Microservice A to Microservice B and why relaying is not sufficient? What are your trying to achieve by exchanging tokens for inter-service requests?
We have a set up very similar to what is described in this Nordic APIs entry. The short version is that external callers use an opaque token, but once the request goes through our gateway, every microservice gets a JWT representation of the same token. We had to implement a custom endpoint to to perform the opaque to JWT exchange. When services need to interact with one another, we do not exchange the token when A needs to call B, we simply relay the token. Either the RestTemplate or Feign client will automatically forward the token from A to B. Thus, context is not lost.
Now, if we wanted to control access, the JWT could specify a collection of audience values or we could enforce access via scopes. We are actually doing a combination of the two depending on the use case.
Exchanging tokens is not a cheap operation, in fact it's quite expensive at scale and should really consider why you need to do a token exchange for intra-service communication. If you every API request is going result in service A calling service B and you have to make a token exchange, you're going to have ensure that your authorization service can handle that type of workload. Lastly, the IETF token exchange is still draft status, and has changed quite a bit in its evolution, so I would not expect much in the way of implementation advice until the spec gets closer to finalization.
回答2:
I think this is something you might try.
In my project we also use OAuth2, Eureka, Ribbon for microservices to communicate each other. In order to use Ribbon with OAuth2, the approach we took was bit different.
First we leave the restTemplate untouched.
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
However, we created FeignClientIntercepter implementing RequestIntercepter which sets authorization tokens for OAuth when making a request via restTemplate.
@Component
public class FeignClientInterceptor implements RequestInterceptor {
private static final String AUTHORIZATION_HEADER = "Authorization";
private static final String BEARER_TOKEN_TYPE = "Jwt";
@Override
public void apply(RequestTemplate template) {
SecurityContext securityContext = SecurityContextHolder.getContext();
Authentication authentication = securityContext.getAuthentication();
if (authentication != null && authentication
.getDetails() instanceof OAuth2AuthenticationDetails) {
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication
.getDetails();
template.header(AUTHORIZATION_HEADER,
String.format("%s %s", BEARER_TOKEN_TYPE, details.getTokenValue()));
}
}
}
来源:https://stackoverflow.com/questions/34905628/how-to-implement-oauth2-token-exchange-with-spring-cloud-security