如何将 Web 应用程序中 <img> 标记中使用的图像限制为 "anyone within corporation"?

How to restrict an image used in an <img> tag in a Web App to "anyone within corporation"?

我可以限制 Web App 的使用,但我不能限制对 标签中引用的图像的访问。

现在我正在使用 Google Apps 脚本开发 Web 应用程序。 这是公司的内部应用,我必须仔细设置资产的访问权限。

对于应用程序本身,从脚本编辑器的部署为网络应用程序框,我将执行应用程序设置为我 (bar@foo.example.com)有权访问该应用程序的人 作为 FOO 公司内的任何人

(这里假设我属于 FOO 公司。我的 Google 帐户是 bar@foo.example.com)

现在,当我使用FOO的Google帐号登录时,我可以成功访问Web App。 但是当我没有登录时,我无法访问它。 这就是我想要的。

但是当我设置 标签显示 Google 驱动器中的 jpeg 文件时,我必须将图像文件的 Share 设置为 Internet 上任何拥有此 link 的人都可以查看

当我在Web App项目的HTML中设置标签,并设置jpeg文件的ShareFOO Corporation -- 这个link组里的任何人都可以查看标签将不起作用(将返回错误403)。

我想限制图像的访问权限以及网络应用程序。 我该怎么做?


如何重现

放入jpeg文件

  1. 将任何 JPEG(假设它是 baz.jpeg)文件放入 Google 驱动器
  2. right-click 和 select 分享
  3. Get link 设置为 FOO Corporation -- 此组中的任何人link 都可以查看
  4. 双击baz.jpeg预览
  5. select => 新开window
  6. 假设 URL 是 https://drive.google.com/file/d/xxxx_object ID_xxxx/view。记录 object ID(此处 xxxx_object ID_xxxx)

  1. 创建一个新的无标题脚本项目
  2. 创建一个 index.html 文件并添加代码:
<html>

  <head>
    <base target="_top">
  </head>

  <body>
    <img src="https://drive.google.com/uc?id=xxxx_object ID_xxxx" width="30" height="30" alt="error alt text">
  </body>

</html>

创建Code.gs

  1. 将Code.gs改成下面的代码

    function doGet() {
      return HtmlService.createHtmlOutputFromFile('index');
    }

  1. 保存

将其发布为 Web 应用程序

  1. Select 菜单项 发布 > 部署为网络应用程序
  2. 部署为网络应用程序框中,设置项目版本:新建
  3. 设置执行应用程序为:Me (bar@FOO.example.com)
  4. 谁有权访问该应用程序:设置为FOO公司内的任何人
  5. 单击部署
  6. 单击消息中的文本 最新代码 测试您的最新代码的网络应用程序

结果--错误

    Google (logo)
    403. That’s an error.
    We're sorry, but you do not have access to this page. That’s all we know.

更改图像文件的访问权限

  1. 在Google驱动器中,找到镜像文件baz.jpeg
  2. right-click 和 select 分享
  3. Get link 设置为 Internet 上使用此 link 的任何人都可以查看
  4. 打开带有破损图片图标的 Web App 选项卡并重新加载
  5. 你看到正确的图像

我想做什么?

我想限制图片的访问权限以及Web应用程序(只有FOO公司的用户才能访问)。 我该怎么做?

403 禁止

/uc 端点,当文件权限设置为“在此组中”时,returns 一个 403 Forbidden 响应,即使您已登录 G Suite 帐户。


解决方法

您可以实现将 HTMLImageElement 属性设置为 src 动态附加到图像数据(来自字节的 base-64 编码字符串)的流程。有了这个,您可以限制对 Web 应用程序和图像的访问,并且仍然能够加载它。

部署 Web App 时,请确保部署对文件具有足够的访问权限,例如,当文件具有“此组中具有此 link 的任何人都可以查看”权限时:

Execute the app as: Me
Who has access to the app: Anyone within [org]

下面是一个小的概念验证,包括一个 server-side 实用程序和一个示例 HTML 文件。

<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>

<body>

    <script>

        const asyncGAPIv2 = ({
            funcName,
            onFailure = console.error,
            onSuccess,
            params = []
        }) => {
            return new Promise((res, rej) => {
                google.script.run
                    .withSuccessHandler(data => {
                        typeof onSuccess === "function" && onSuccess(data);
                        res(data);
                    })
                    .withFailureHandler(error => {
                        typeof onFailure === "function" && onFailure(error);
                        rej(error);
                    })
                [funcName].apply(null, params);
            });
        };

        const fetchAndAppendImage = async ({ parent = document.body, id }) => {

            const data = await asyncGAPIv2({
                funcName: "getImageFromDrive",
                params: [{ id, token }]
            });
        
            const img = document.createElement("img");
            img.src = data;

            parent.append(img);
        };

        (async () => await fetchAndAppendImage({ id: "id here" }))();
    </script>

</body>

</html>

您可以将 id 传递给 server-side 实用程序,使用 getFileById 获取文件(本机身份验证流程将确保请求者无法访问他们无权访问的文件) , 并通过执行以下操作形成图像数据字符串:

  1. 通过更改 getBlob to getBytesFile 实例中提取原始字节。
  2. 使用 base64Encode method of the Utilities service to convert bytes to a base-64 encoded string and prepend data:image/png;base64, (Data URL 方案)。如果您的图片有另一个MIME-type,请相应修改。
/**
 * @summary gets an image from Google Drive
 * 
 * @param {{
 *  id : string
 * }}
 * 
 * @returns {string}
 */
const getImageFromDrive = ({ id }) => {

    try {
      
      const file = DriveApp.getFileById(id);
      const bytes = file.getBlob().getBytes();
      
      const data = `data:image/png;base64,${Utilities.base64Encode(bytes)}`;

      return data;

    } catch (error) {
        console.warn(error);
        return "";
    }
};

备注

  1. 要使上述工作正常,您必须拥有更新的 V8 runtime enabled
  2. 可能的原因是登录到多个帐户,因为试图通过 /uc 具有“在此组中”权限的端点访问文件,而 仅 [=64] =] 登录 G Suite 帐户没有授权问题。

和与他的讨论,作为一种方法,我想建议使用Templated HTML。这些方法的起源是 Oleg Valter 的回答。当你的脚本修改后,会变成下面这样。

Google Apps 脚本端:

从:
function doGet() {
  return HtmlService.createHtmlOutputFromFile('index');
}
到:
function doGet() {
  const id = "###";  // <--- Please set the file ID of the image file.

  const html = HtmlService.createTemplateFromFile('index');
  html.image = getImageFromDrive({id: id});  // <--- This function is from Oleg Valter's answer.
  return html.evaluate();
}

HTML 边:

从:
<img src="https://drive.google.com/uc?id=xxxx_object ID_xxxx" width="30" height="30" alt="error alt text">
到:
<img src="<?!= image ?>" width="30" height="30" alt="error alt text">

注:

  • 使用了getImageFromDrive函数
  • 当您修改Web Apps的脚本时,请重新部署Web Apps为新版本。由此,最新的脚本被反​​映到Web Apps。请注意这一点。

参考: