Cloud Run + Cloud Endpoints +服务帐户身份验证–在curl中有效,但在JS中使用访存API时无效

问题描述

我已经配置了执行GCF的云端点。当云运行服务允许allUsers调用API时,一切正常。

一旦我删除allUsers并使用服务帐户进行身份验证,云运行控制台中将显示403错误

请求未通过验证。允许未经身份验证的调用或设置适当的Authorization标头。在https://cloud.google.com/run/docs/securing/authenticating

中了解更多信息

Chrome JS控制台显示以下错误消息:

可从原始位置访问“ https://.run.app/do-this&key=” “ http://0.0.0.0:8080”已被CORS政策阻止:未响应预检请求 通过访问控制检查:请求中不存在“ Access-Control-Allow-Origin”标头 资源。如果不透明的响应满足您的需求,请将请求的模式设置为“ no-cors”以获取 禁用了CORS的资源。

这是我在浏览器中运行的JS代码

        let options: Requestinit = {
            headers: {
                'Authorization': `Bearer ${token}`,},}
        
        const result = await fetch(fetchURL,options);

使用相同令牌运行curl时,我得到了预期的响应

curl -H "Authorization: Bearer ${token}" 'https://<my-api>.run.app/do-this&key=<key>'

出于完整性考虑,这里也是端点yaml

swagger: '2.0'
info:
  title: My first widget
  description: This is a great widget
  version: 1.0.0
host: <my-api>.run.app
schemes:
  - https
produces: 
  - application/json
paths:
  /do-this:
    get:
      summary: Do-this
      operationId: doit
      x-google-backend:
        address: https://<project-id>.cloudfunctions.net/do-that
      responses:
        '200':
          description: A successful response.
          schema:
            type: string
        '403':
          description: An error occurred
          schema:
            type: string
      security:
        - api_key: []

securityDeFinitions:
  # This section configures basic authentication with an API key.
  api_key:
    type: "apiKey"
    name: "key"
    in: "query"

更新esp的命令:

gcloud run services update <my-api> --set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--rollout_strategy=managed,--cors_preset=basic" --project=<project-id> --platform=managed --region=europe-west1

更新

启用cors浏览器端无济于事。

Google文档提到应该call from outside GCP

如果您要从无法访问计算元数据的计算实例(例如您自己的服务器)调用服务,则必须手动生成适当的令牌:
使用target_audience声明设置为接收服务URL的服务帐户JWT自签名。 将自签名的JWT交换为Google签名的ID令牌,该令牌应将aud声明设置为上述URL。 将ID令牌包括在对服务的请求中的Authorization:Bearer ID_TOKEN标头中。 尽管Cloud Run(完全托管)尚不支持身份识别代理,但是您可以检查身份识别代理示例代码,以获取上述步骤的代码示例。

The end-users section:尽管提及CORS

构建Web应用程序时,必须解决跨域资源共享(CORS)问题。例如,发送的CORS预检请求没有Authorization标头,因此它们在非公共服务上被拒绝。因为预检请求失败,所以主请求也将失败。
解决此问题,您可以将您的Web应用程序和服务托管在同一域中,以避免CORS的预检请求。您可以使用Firebase托管来实现。

我尝试在Firebase托管上托管JS脚本和HTML,但问题仍然存在。

想到的另一个问题是:我是否需要在开放api规范中同时设置OAuth和API密钥身份验证?

更新2

This discussion建议无法将Cloud Run与支持CORS的身份验证一起使用。我还想知道为什么会卷曲。我正在使用服务帐户令牌进行身份验证,而不是最终用户

解决方法

未为Cloud Endpoint激活cors。像这样更新您的openAPI规范

IloCplex mycplex = new IloCplex();

int Ncd=2;
int Nbv=6;
int T=4;

IloNumVar[][][][] y = new IloIntVar[Ncd][][][];
        for(int i=0; i<y.length;i++) {
            y[i]= new IloIntVar[Ncd][][];
            for(int j=0; j<y[i].length;j++) {
                y[i][j]= new IloIntVar[Nbv+1][];
                for(int k=1; k<y[i][j].length; k++) {
                    

                        y[i][j][k]= mycplex.boolVarArray(T+1);
for(int t=1; t<T; t++)
{
                        IloRange myConstraint2 = mycplex.addEq(y[i][j][k][t],1);
mycplex.add(myConstraint2);
}


            }
        }
    }

mycplex.solve();


for (int i = 0; i < Ncd; i++) {

                for (int j = 0; j < Ncd; j++) {
                   for (int k = 1; k < Nbv; k++) {
                           for(int t=1; t<T; t++)
                      System.out.printf("valeur = "+mycplex.getValue(y[i][j][k][t]));
                      }
                   }
                } 

或按照错误消息的说明设置swagger: '2.0' info: title: My first widget description: This is a great widget version: 1.0.0 host: <my-api>.run.app x-google-endpoints: - name: <my-api>.run.app allowCors: True ... ... ... 来签入您的电话。

,

我通过以下方式使其工作:

  1. oauth作为安全性定义添加到OpenAPI规范中,并将其与每个API路径的api密钥一起使用
  2. 使用--set-env-vars="^|^ENDPOINTS_SERVICE_NAME=<my-api>.run.app|ESP_ARGS=--cors_preset=basic,--rollout_strategy=managed"部署端点
  3. 在Cloud Function中,将Access-Control-Allow-Origin设置为空字符串''res.setHeader("Access-Control-Allow-Origin",'')
  4. 允许allUsers可以访问Cloud Run容器

每个人都可以访问Cloud Run容器时,端点将负责身份验证。

令我惊讶的是,CF自动在mydomain.com标头中添加了呼叫主机(例如*)和Access-Control-Allow-Origin。此标头中不允许使用多个项目,因此我放弃了mydomain.com并保留了*

我将尝试使用不同的选项,完成后将提供所有步骤的操作方法。任何意见/建议都非常感谢!

更新

深入研究之后,我了解到Access-Control-Allow-Origin会自动添加const cors = require('cors')({origin: true});

对于我的用例,我不需要CF内的cors,因为只能从Cloud Run ESP中访问它们。

因此重要的步骤是:

  • 将oauth添加到OpenAPI规范
  • 通过ESP_ARGS启用cors
  • 允许allUsers可以访问云运行容器