我想知道是否有人有一个例子来了解如何使用Spring Cloud Security(使用OAuth2)实现“令牌交换”技术。
目前,我已经在微服务环境中使用ZuulProxy实现了令牌中继技术来中继OAuth2令牌并实现SSO。这很好,但意味着每个微服务都使用相同的clientId(在ZuulProxy设置中指定,因为ZuulProxy仅使用authorization_code授权类型和提供的clientId中继令牌)。然而,对于微服务内部调用,我想“交换”令牌。这意味着在某些情况下,ZuulProxy中继的令牌不是我需要用来验证/授权微服务A作为微服务B客户端的令牌。
Spring Cloud参考留档目前表示:“基于Spring Boot和Spring Security OAuth2,我们可以快速创建实现通用模式的系统,如单点登录、令牌中继和令牌交换。”(http://cloud.spring.io/spring-cloud-security/spring-cloud-security.html)
我猜参考留档中的“令牌交换”意味着OAuth2扩展的实现,在本规范中解释,这基本上是我需要的:https://datatracker.ietf.org/doc/html/draft-ietf-oauth-token-exchange-03
正如我所说,我知道如何使用SSO和令牌中继,但我无法在参考留档中看到关于如何实现“令牌交换”的进一步解释。我也找不到实现示例。
有人知道我在哪里可以找到更多信息或示例吗?
非常感谢!
我很好奇为什么您需要“交换”令牌来从微服务A到微服务B进行调用,为什么中继是不够的?您试图通过为服务间请求交换令牌来实现什么?
我们的设置与这个Nordic API条目中描述的非常相似。简短的版本是外部调用者使用不透明的令牌,但是一旦请求通过我们的网关,每个微服务都会获得相同令牌的JWT表示。我们必须实现一个自定义endpoint来执行不透明到JWT的交换。当服务需要相互交互时,当A需要调用B时,我们不会交换令牌,我们只是中继令牌。无论是RestTemplate还是Feegnclient都会自动将令牌从A转发到B。因此,上下文不会丢失。
现在,如果我们想控制访问,JWT可以指定一组受众值,或者我们可以通过范围强制访问。我们实际上是根据用例将两者结合起来。
交换令牌不是一个便宜的操作,事实上它在规模上相当昂贵,并且应该真正考虑为什么需要为服务内通信进行令牌交换。如果您的每个API请求都导致服务A调用服务B,并且您必须进行令牌交换,那么您将必须确保您的授权服务能够处理这种类型的工作负载。最后,IETF令牌交换仍然是草稿状态,并且在其演变过程中发生了很大变化,因此在规范接近最终确定之前,我不会对实现建议有太多期望。
我想这是你可以尝试的东西。
在我的项目中,我们还使用OAuth2、Eureka、Ribbon进行微服务相互通信。为了将Ribbon与OAuth2一起使用,我们采取的方法有点不同。
首先,我们保持restTemplate不变。
@LoadBalanced
@Bean
public RestTemplate restTemplate() {
但是,我们创建了实现Request estIntercepter的FEGNClientIntercepter,它在通过restTemplate发出请求时为OAuth设置授权令牌。
@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()));
}
}
}