You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Google Web Apps 获取用户邮箱同时以所有者身份运行脚本问题咨询

首选解决方案(最简实现)

首先纠正一个常见认知误区:只有当Web App的访问权限设置为「任何人,甚至匿名用户」时,部署为所有者运行才会拿不到访问用户的邮箱。只要你按以下配置部署,不需要做额外开发就能解决所有问题:

  1. Web App部署配置
  • 执行身份:我(脚本所有者)
  • 有权访问的人:拥有Google账号的用户(如果仅限内部使用可选「我的组织内的用户」)
  1. 核心代码逻辑
    你不需要修改原有校验逻辑,因为脚本以所有者身份运行,访问存储权限的谷歌表格不需要给用户开通任何权限,同时可以正常通过Session.getActiveUser().getEmail()获取访问用户的真实邮箱,示例代码如下:
function doGet(e) {
  const userEmail = Session.getActiveUser().getEmail();
  const userGroup = validate(userEmail);
  if (userGroup === "Not Authorized") {
    return HtmlService.createHtmlOutput("无访问权限");
  }
  // 按权限组返回对应过滤后的数据即可
  return HtmlService.createHtmlOutput(`权限校验通过,所属权限组:${userGroup}`);
}

function validate(email){
  const sheet = SpreadsheetApp.openById("你的表格ID").getSheetByName("用户权限表");
  try{
    const group = getRowsData(sheet).find(e => e.userEmail === email).securityGroup;
    return group;
  } catch(e){
    return "Not Authorized";
  }
}

备选方案(适用于必须拆分用户/所有者身份运行的场景)

你提到的参考方案就是类似的双Web App思路,做安全优化后完全适配高保密要求。如果你后续有逻辑必须以访问用户身份运行(比如操作用户自己的云端硬盘资源),可以优化你之前想到的双Web App方案,完全解决安全问题:

  1. 核心思路
  • 部署两个Web App,都放在同一个脚本项目下共享配置
  • 前端App以用户身份运行,负责获取用户邮箱,生成带签名的请求
  • 服务端App以所有者身份运行,部署为仅限所有者访问,负责校验请求合法性、处理权限查询等需要所有者权限的逻辑
  1. 具体实现步骤
    首先在脚本项目的「项目设置」→「脚本属性」中添加一条配置:名称为SIGN_SECRET,值为一串自定义的随机复杂字符串,用作签名校验的密钥,不要泄露给任何人。

前端App代码(部署配置:执行身份为访问用户,访问权限为目标用户群)

function doGet(e) {
  const userEmail = Session.getActiveUser().getEmail();
  const timestamp = Date.now();
  const secret = PropertiesService.getScriptProperties().getProperty("SIGN_SECRET");
  // 生成请求签名
  const signature = Utilities.computeHmacSha256Signature(`${userEmail}_${timestamp}`, secret)
    .map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2))
    .join('');
  // 调用服务端接口
  const serviceRes = UrlFetchApp.fetch("你的服务端Web App部署链接", {
    method: "POST",
    contentType: "application/json",
    payload: JSON.stringify({
      email: userEmail,
      timestamp: timestamp,
      signature: signature
    })
  });
  const resData = JSON.parse(serviceRes.getContentText());
  if (resData.code !== 0) {
    return HtmlService.createHtmlOutput(resData.msg);
  }
  // 后续处理用户侧逻辑
  return HtmlService.createHtmlOutput(`权限校验通过,所属权限组:${resData.data.group}`);
}

服务端App代码(部署配置:执行身份为所有者,访问权限为仅限我自己)

function doPost(e) {
  try {
    const params = JSON.parse(e.postData.contents);
    const {email, timestamp, signature} = params;
    // 校验请求有效期,防止重放攻击,这里设置为5分钟
    if (Date.now() - timestamp > 5 * 60 * 1000) {
      return ContentService.createTextOutput(JSON.stringify({code: 1, msg: "请求已过期"}));
    }
    // 校验签名合法性
    const secret = PropertiesService.getScriptProperties().getProperty("SIGN_SECRET");
    const validSignature = Utilities.computeHmacSha256Signature(`${email}_${timestamp}`, secret)
      .map(byte => ('0' + (byte & 0xFF).toString(16)).slice(-2))
      .join('');
    if (validSignature !== signature) {
      return ContentService.createTextOutput(JSON.stringify({code: 2, msg: "请求非法"}));
    }
    // 权限校验
    const userGroup = validate(email);
    if (userGroup === "Not Authorized") {
      return ContentService.createTextOutput(JSON.stringify({code: 3, msg: "无访问权限"}));
    }
    // 后续可扩展其他需要所有者权限运行的逻辑
    return ContentService.createTextOutput(JSON.stringify({code: 0, data: {group: userGroup}}));
  } catch (err) {
    return ContentService.createTextOutput(JSON.stringify({code: 99, msg: "服务异常"}));
  }
}

function validate(email){
  const sheet = SpreadsheetApp.openById("你的表格ID").getSheetByName("用户权限表");
  try{
    const group = getRowsData(sheet).find(e => e.userEmail === email).securityGroup;
    return group;
  } catch(e){
    return "Not Authorized";
  }
}

该方案完全满足高保密要求:

  • 服务端App仅限所有者访问,就算链接泄露其他人也无法调用
  • 请求带签名和有效期,就算请求被截获也无法篡改或复用
  • 不需要给用户开通任何权限表格的访问权限

内容的提问来源于stack exchange,提问作者Niya

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.26 13:54:05