在我当前的项目中,我有一个应用程序,其中有一个小图形部分,用户使用SSO进行身份验证,以及一部分纯API,用户使用授权标头进行身份验证。
例如:
/ping-other-service
。 /api/ping-other-service
使用持票人令牌作为所有云原生,我们的应用与使用JWT令牌(UAA)使用相同SSO提供商的其他服务进行通信,因此我认为自according to the documentation以来我们一直使用OAuth2RestTemplate
它可以神奇地插入身份验证凭据。它确实对使用SSO进行身份验证的所有端点执行此操作。但是,当我们使用通过承载令牌进行修改的端点时,它不会填充其余模板。
我的理解from the documentation是@EnableOAuth2Client
只会从SSO登录中提取令牌,而不是auth标头?
请求失败及其作用:
curl -H "Authorization: Bearer <token>" http://localhost/api/ping-other-service
成功的请求及其作用:
为了解决这个问题,我最终创建了以下怪物,如果它不能从授权标题中获取,它将从OAuth2ClientContext
中提取令牌。
@PostMapping(path = "/ping-other-service")
public ResponseEntity ping(@PathVariable String caseId, HttpServletRequest request, RestTemplate restTemplate) {
try {
restTemplate.postForEntity(adapterUrl + "/webhook/ping", getRequest(request), Map.class);
} catch (HttpClientErrorException e) {
e.printStackTrace();
return new ResponseEntity(HttpStatus.SERVICE_UNAVAILABLE);
}
return new ResponseEntity(HttpStatus.OK);
}
private HttpEntity<?> getRequest(HttpServletRequest request) {
HttpHeaders headers = new HttpHeaders();
headers.set("Authorization", "Bearer " + getRequestToken(request));
return new HttpEntity<>(null, headers);
}
private String getRequestToken(HttpServletRequest request) {
Authentication token = new BearerTokenExtractor().extract(request);
if (token != null) {
return (String) token.getPrincipal();
} else {
OAuth2AccessToken accessToken = oAuth2ClientContext.getAccessToken();
if (accessToken != null) {
return accessToken.getValue();
}
}
throw new ResourceNotFound("No valid access token found");
}
答案 0 :(得分:1)
在let modified = text.replacingOccurrences(of: "\\s", with: " ", options: .regularExpression)
资源中有一个传入令牌,但由于您使用的是JWT,资源服务器可以在不调用auth服务器的情况下进行身份验证,因此没有/api/**
只是等待等待您可以在令牌中继中重用上下文(如果您使用OAuth2RestTemplate
则会有一个)。您可以非常轻松地创建一个,并将传入的令牌拉出UserInfoTokenServices
。例如:
SecurityContext
您可以将该方法转换为 @Autowired
private OAuth2ProtectedResourceDetails resource;
private OAuth2RestTemplate tokenRelayTemplate(Principal principal) {
OAuth2Authentication authentication = (OAuth2Authentication) principal;
OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) authentication.getDetails();
details.getTokenValue();
OAuth2ClientContext context = new DefaultOAuth2ClientContext(new DefaultOAuth2AccessToken(details.getTokenValue()));
return new OAuth2RestTemplate(resource, context);
}
(在@Bean
中)并根据需要为模板注入@Scope("request")
。
在Spring Cloud Security中有一些自动配置和实用程序类来帮助处理这种模式,例如:https://github.com/spring-cloud/spring-cloud-security/blob/master/spring-cloud-security/src/main/java/org/springframework/cloud/security/oauth2/client/AccessTokenContextRelay.java
答案 1 :(得分:0)
我在开发Spring资源服务器时遇到了这个问题,我需要将OAuth2令牌从请求传递给restTemplate以调用下游资源服务器。两个资源服务器都使用相同的auth服务器,我发现Dave的链接很有帮助,但我不得不挖掘一下以了解如何实现它。我最终找到了文档here,结果显示实现非常简单。我正在使用@EnableOAuth2Client
,因此我必须使用注入的restTemplate
创建OAuth2ClientContext
bean并创建适当的资源详细信息。就我而言,它是ClientCredentialsResourceDetails
。感谢Dave的所有伟大作品!
@Bean
public OAuth2RestOperations restTemplate (OAuth2ClientContext context) {
ClientCredentialsResourceDetails details = new ClientCredentialsResourceDetails();
// Configure the details here
return new OAuth2RestTemplate(details, context)
}
答案 2 :(得分:0)
@Dave Syer 我的UAA服务还是oauth2客户端,它需要中继来自Zuul的JWT令牌。通过以下方式配置oauth2客户端时
@Configuration
@EnableOAuth2Client
@RibbonClient(name = "downstream")
public class OAuthClientConfiguration {
@Bean
public OAuth2RestTemplate restTemplate(OAuth2ProtectedResourceDetails resource, OAuth2ClientContext context) {
return new OAuth2RestTemplate(resource, context);
}
}
我确实从下游服务获得401响应,因为我的访问令牌的有效性非常短,并且AccessTokenContextRelay
不会更新传入的访问令牌(Zuul确实通过刷新令牌来更新过期的访问令牌)。 / p>
OAuth2RestTemplate#getAccessToken
将永远不会获取新的访问令牌,因为isExpired
存储的访问令牌上的AccessTokenContextRelay
会丢弃有效性和刷新令牌信息。
如何解决?