从 ComputeEngineCredentials 获取 ServiceAccountCredentials 进行用户模拟
Get ServiceAccountCredentials from ComputeEngineCredentials to do user impersonation
我正在部署一个 Java API,它通过 Domain-widge Delegation 模拟用户来访问用户的日历。为此,我创建了一个服务帐户,完成了委派并赋予它正确的权限和访问用户日历的权限。
在本地开发时,我一直在使用 JSON 格式的服务帐户下载密钥,并使用 GOOGLE_APPLICATION_CREDENTIALS 环境变量指向它。在我的代码中,我使用 Java 客户端库创建这样的凭据:
@Bean
@Qualifier("userCredentials")
public GoogleCredentials impersonateCalendarOwner() throws IOException {
final List<String> scopes = Collections.singletonList(CalendarScopes.CALENDAR);
return ((ServiceAccountCredentials) GoogleCredentials.getApplicationDefault())
.toBuilder()
.setServiceAccountUser(GOOGLE_CALENDARS_OWNER)
.build()
.createScoped(scopes);
}
这在本地工作正常,但是当 运行 在云端 运行 我得到:
nested exception is java.lang.ClassCastException:
class com.google.auth.oauth2.ComputeEngineCredentials cannot be cast to
class com.google.auth.oauth2.ServiceAccountCredentials
经过几个小时的调试,我想我终于明白了 getApplicationDefault
是如何工作的。我认为本地发生的事情是这样的:
- getApplicationDefault 首先查看环境变量 GOOGLE_APPLICATION_CREDENTIALS,因为它已设置,所以它会从该文件中读取凭据
- 因为该文件是一个服务帐户密钥文件,所以它创建了一个 ServiceAccountCredentials 实例,因此“转换”成功。
而在 Cloud 运行 中,它是这样发生的:
- getApplicationDefault 首先查看环境变量 GOOGLE_APPLICATION_CREDENTIALS,但在 Cloud 运行 上未设置
- 因此它回退到服务帐户身份,修订版是 运行,因为通过从元数据服务器获取凭据并将它们 returns 作为 ComputeEngineCredentials 的实例(因为元数据服务器不不要分发服务帐户的密钥)。
- 然后它尝试将 ComputeEngineCredentials 转换为 ServiceAccountCredentials,这显然不起作用。
所以现在我的问题仍然存在:
- 如何将一些 ComputeEngineCredentials 转换为 ServiceAccountCredentials?
- 是否有其他方法可以将凭据作为 ServiceAccountCredentials 的实例获取?
- 是否有其他不需要 ServiceAccountCredentials 实例的用户模拟方式?
我目前的想法是使用我获得的 ComputeEngineCredentials,在服务启动时从 Secret Manager 获取服务帐户 JSON 密钥文件,然后将该文件传递给 ServiceAccountCredentials.fromStream 但感觉就像一个额外的步骤,因为我将使用的 ComputeEngineCredentials 已经是我将为其获取密钥的同一服务帐户的凭据。
您需要将 Cloud 运行 服务告知 run as your service account:
gcloud run deploy --image IMAGE_URL --service-account SERVICE_ACCOUNT
实例元数据(元数据服务器)提供的凭据不包括签署请求所需的服务帐户私钥。
因此,您尝试使用的服务帐户模拟代码将不起作用。您将需要使用包含私钥的实际服务帐户 JSON。
我的建议是设置默认服务帐户,使其具有访问 Secret Manager 的角色。将服务帐户 JSON 密钥文件作为数据存储在 Secret Manager 中。在您的程序启动时获取服务帐户 JSON 内容并继续您的模拟代码。
我正在部署一个 Java API,它通过 Domain-widge Delegation 模拟用户来访问用户的日历。为此,我创建了一个服务帐户,完成了委派并赋予它正确的权限和访问用户日历的权限。
在本地开发时,我一直在使用 JSON 格式的服务帐户下载密钥,并使用 GOOGLE_APPLICATION_CREDENTIALS 环境变量指向它。在我的代码中,我使用 Java 客户端库创建这样的凭据:
@Bean
@Qualifier("userCredentials")
public GoogleCredentials impersonateCalendarOwner() throws IOException {
final List<String> scopes = Collections.singletonList(CalendarScopes.CALENDAR);
return ((ServiceAccountCredentials) GoogleCredentials.getApplicationDefault())
.toBuilder()
.setServiceAccountUser(GOOGLE_CALENDARS_OWNER)
.build()
.createScoped(scopes);
}
这在本地工作正常,但是当 运行 在云端 运行 我得到:
nested exception is java.lang.ClassCastException:
class com.google.auth.oauth2.ComputeEngineCredentials cannot be cast to
class com.google.auth.oauth2.ServiceAccountCredentials
经过几个小时的调试,我想我终于明白了 getApplicationDefault
是如何工作的。我认为本地发生的事情是这样的:
- getApplicationDefault 首先查看环境变量 GOOGLE_APPLICATION_CREDENTIALS,因为它已设置,所以它会从该文件中读取凭据
- 因为该文件是一个服务帐户密钥文件,所以它创建了一个 ServiceAccountCredentials 实例,因此“转换”成功。
而在 Cloud 运行 中,它是这样发生的:
- getApplicationDefault 首先查看环境变量 GOOGLE_APPLICATION_CREDENTIALS,但在 Cloud 运行 上未设置
- 因此它回退到服务帐户身份,修订版是 运行,因为通过从元数据服务器获取凭据并将它们 returns 作为 ComputeEngineCredentials 的实例(因为元数据服务器不不要分发服务帐户的密钥)。
- 然后它尝试将 ComputeEngineCredentials 转换为 ServiceAccountCredentials,这显然不起作用。
所以现在我的问题仍然存在:
- 如何将一些 ComputeEngineCredentials 转换为 ServiceAccountCredentials?
- 是否有其他方法可以将凭据作为 ServiceAccountCredentials 的实例获取?
- 是否有其他不需要 ServiceAccountCredentials 实例的用户模拟方式?
我目前的想法是使用我获得的 ComputeEngineCredentials,在服务启动时从 Secret Manager 获取服务帐户 JSON 密钥文件,然后将该文件传递给 ServiceAccountCredentials.fromStream 但感觉就像一个额外的步骤,因为我将使用的 ComputeEngineCredentials 已经是我将为其获取密钥的同一服务帐户的凭据。
您需要将 Cloud 运行 服务告知 run as your service account:
gcloud run deploy --image IMAGE_URL --service-account SERVICE_ACCOUNT
实例元数据(元数据服务器)提供的凭据不包括签署请求所需的服务帐户私钥。
因此,您尝试使用的服务帐户模拟代码将不起作用。您将需要使用包含私钥的实际服务帐户 JSON。
我的建议是设置默认服务帐户,使其具有访问 Secret Manager 的角色。将服务帐户 JSON 密钥文件作为数据存储在 Secret Manager 中。在您的程序启动时获取服务帐户 JSON 内容并继续您的模拟代码。