我有一个授权服务器和一个(独特的应用程序)资源服务器。 目前,我不想使用JWT令牌。 授权服务器公开一个 / user / me 端点以检索呼叫者的身份验证:
@GetMapping("/user/me")
public Principal me(final Principal principal) {
return principal;
}
资源服务器调用此终结点,在HTTP标头中提供访问令牌。这样,在给定访问令牌的情况下,资源服务器将检索源自此令牌的身份验证。 当来自资源服务器的HTTP请求命中此端点(已使用属性 security.oauth2.resource.user-info-uri 配置)时,它将通过 OAuth2AuthenticationProcessingFilter 。 借助 TokenExtractor ,此过滤器从标头中提取访问令牌,并且尝试通过 OAuth2AuthenticationManager 对生成的(预)身份验证进行身份验证:
Authentication authResult = authenticationManager.authenticate(authentication);
由于我的资源服务器和授权服务器是两个不同的服务器,因此身份验证管理器是 UserInfoTokenServices 。
OAuth2Authentication auth = tokenServices.loadAuthentication(token);
UserInfoTokenServices 对授权服务器的 / user / me 端点进行REST调用,并将检索到的内容映射到新创建的 OAuth2Authentication 返回给 authenticate()。
我的问题是这种映射似乎是错误的。 / user / me 返回的REST响应具有用户的权限和详细信息,指示已通过身份验证的标志,用于用户身份验证的部分( userAuthentication ),一个部分用于客户端身份验证( oauth2Request ),以及其他几个条目。 由 UserInfoTokenServices.extractAuthentication()完成的映射将创建一个新的 OAuth2Authentication ,该存储空间为一个空的storageRequest(代表客户端身份验证的部分)。客户信息(例如范围和资源ID)将丢失。实际上,返回到 authenticate()会跳过以下测试:
Collection<String> resourceIds = auth.getOAuth2Request().getResourceIds();
if (resourceId != null && resourceIds != null && !resourceIds.isEmpty() && !resourceIds.contains(resourceId)) {
throw new OAuth2AccessDeniedException("Invalid token does not contain resource id (" + resourceId + ")");
}
因为 resourceIds 为空,因为如上所述,auth是一个新的 OAuth2Authentication ,它丢失了身份验证的客户端部分。这意味着在我的授权服务器中使用特定资源注册的客户端实际上可以访问任何资源。例如。客户
clientDetailsServiceConfigurer
.inMemory()
.withClient("client")
...
.resourceIds("foo")
...
可以使用 bar id来访问资源。
为什么 UserInfoTokenServices.extractAuthentication()这样做,如何解决我的问题?
要完全准确, UserInfoTokenServices.extractAuthentication()不会完全丢失信息,因为REST调用对 / user / me 的响应存储在新的 OAuth2Authentication 的详细信息部分:
token.setDetails(map);
但这是没有用的,因为代码不会在此处搜索资源ID。 有关信息,我正在使用代码授予类型。