首页 > 解决方案 > 无需身份验证调用 GCloud 函数

问题描述

我对使用 gcloud 功能很陌生。

我有一个谷歌云函数,它应该启动一个 VM 实例,但我想在发送 HTTP 请求时从一个没有身份验证的网站调用这个函数(现在)。我怎样才能做到这一点?

我也不需要发送任何参数,只需要调用函数

这是我的一个简单网站调用云功能的代码,虽然这似乎使用了我不想做的身份验证,但我在控制台中创建该功能时禁用了身份验证:

<!DOCTYPE html>
<html lang="en">

<body>
    <div class="bordered-text-block">
        <h1>VM Controls</h1>
        <button id="id_start_button" onclick="startVM()">Start
        </button>
        <button id="id_stop_button" onclick="stopVM()">Stop
        </button>
    </div>

    <script>
        function startVM() {
            const fetch = require('node-fetch');

            // TODO(developer): set these values
            const REGION = '<my region>';
            const PROJECT_ID = '<project name>';
            const RECEIVING_FUNCTION = '<function name>';

            // Constants for setting up metadata server request
            // See https://cloud.google.com/compute/docs/instances/verifying-instance-identity#request_signature
            const functionURL = `https://${REGION}-${PROJECT_ID}.cloudfunctions.net/${RECEIVING_FUNCTION}`;
            const metadataServerURL =
                'http://metadata/computeMetadata/v1/instance/service-accounts/default/identity?audience=';
            const tokenUrl = metadataServerURL + functionURL;

            exports.callingFunction = async (req, res) => {
                // Fetch the token
                const tokenResponse = await fetch(tokenUrl, {
                    headers: {
                        'Metadata-Flavor': 'Google',
                    },
                });
                const token = await tokenResponse.text();

                // Provide the token in the request to the receiving function
                try {
                    const functionResponse = await fetch(functionURL, {
                        headers: { Authorization: `bearer ${token}` },
                    });
                    res.status(200).send(await functionResponse.text());
                } catch (err) {
                    console.error(err);
                    res.status(500).send('An error occurred! See logs for more details.');
                }
            };
        }
    </script>
</body>

</html>

标签: node.jsgoogle-cloud-platformgoogle-cloud-functionsgcloud

解决方案


跟进我的评论,这是最简单的答案。

注意我认为您必须确保您的 Cloud Functions 返回适当的 CORS 标头

  • Access-Control-Allow-Origin: *
  • Access-Control-Allow-Methods: GET

我用 Golang 写了一个云函数:

package p

import (
    "encoding/json"
    "net/http"
)

type Message struct {
    Name string `json:"name"`
}

func WebPage(w http.ResponseWriter, r *http.Request) {
    m := Message{
        Name: "Freddie",
    }
    w.Header().Set("Access-Control-Allow-Origin", "*")
    w.Header().Set("Access-Control-Allow-Methods", "GET")
    w.Header().Set("Content-Type", "application/json")
    err := json.NewEncoder(w).Encode(m)
    if err != nil {
        http.Error(w, err.Error(), 500)
        return
    }
}

我部署了这个功能:

PROJECT="<your-project>"
REGION="<your-region>"
FUNCTION="<your-function>"

gcloud projects create ${PROJECT}

BILLING=$(gcloud beta billing accounts list --format="value(name)")
gcloud beta billing projects link ${PROJECT} --billing-account=${BILLING}

for SERVICE in "cloudbuild" "cloudfunctions"
do
  gcloud services enable ${SERVICE}.googleapis.com \
  --project=${PROJECT}
done

gcloud functions deploy ${FUNCTION} \
--entry-point=WebPage \
--source=. \
--project=${PROJECT} \
--allow-unauthenticated \
--region=${REGION} \
--runtime=go113 \
--trigger-http

您可以在 curl 中获取函数的端点:

URL=$(\
  gcloud functions describe ${FUNCTION} \
  --project=${PROJECT} \
  --format="value(httpsTrigger.url)")

curl \
--silent \
--include \
--header "Accept: application/json" \
--request GET \
${URL}

产量:

HTTP/2 200 
access-control-allow-methods: GET
access-control-allow-origin: *
content-type: application/json
...

{"name":"Freddie"}

然后您可以GET将值放入 HTML 中id

<!DOCTYPE html>
<html lang="en">

<body>
<div id="result">/div>

<script>
    const PROJECT = "<your-project>";
    const REGION = "<your-region>";
    const FUNCTION = "<your-function>";

    const ID = "result";

    const URL = `https://${REGION}-${PROJECT}.cloudfunctions.net/${FUNCTION}`;

    fetch(URL,{
        method: "GET",
        headers: {
            "Accept": "application/json"
        }
    })
    .then(resp => resp.json())
    .then(json => JSON.stringify(json))
    .then(html => document.getElementById(ID).innerHTML = html);
</script>
</body>
</html>

我使用 Caddy 测试了这个(本地):

docker run \
--rm \
--name=caddy \
--interactive --tty \
--publish=80:80 \
--volume=$PWD/index.html:/usr/share/caddy/index.html \
--volume=caddy_data:/data \
caddy

然后浏览http://localhost:80,我得到:

{"name":"Freddie"}

推荐阅读