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

Google Apps Script WebApp多用户并发时UserProperty身份数据被覆盖问题求助

问题根源

你遇到的覆盖问题是Google Apps Script的特性导致的:当WebApp设置为以脚本所有者身份运行时,所有用户触发的脚本执行环境都归属所有者,UserProperties本质是按执行环境的用户隔离的,所以所有用户访问的都是同一个UserProperties实例,用固定key存储自然会互相覆盖。

可行解决方案

首先推荐最稳妥的无状态方案,完全不需要依赖服务端存储用户身份:

方案1:前端传递身份参数(推荐)

核心逻辑是:在doPost拿到用户邮箱后,直接注入到返回的HTML模板中,前端存储后在每次调用google.script.run时主动将邮箱作为参数传递给后端,完全抛弃UserProperties存储。

步骤1:修改doPost逻辑,移除属性存储改为注入模板
function doPost(e) {
  var postContents = e.postData.contents.split("&");
  var postSplit = postContents[16].split("=");
  var activeUserEmail = postSplit[1].replace("%40", "@");
  // 删掉原有的userProperties.setProperty行,不需要服务端存储

  if (activeUserEmail[2] == "s" || activeUserEmail[2] == ".") {
    console.log("Redirecting to Student: " + activeUserEmail);
    const template = HtmlService.createTemplateFromFile('student_form');
    // 将用户邮箱注入模板变量
    template.activeUserEmail = activeUserEmail;
    return template.evaluate()
      .addMetaTag('viewport', 'width=device-width, initial-scale=1')
      .setSandboxMode(HtmlService.SandboxMode.IFRAME)
      .setXFrameOptionsMode(HtmlService.XFrameOptionsMode.ALLOWALL);
  } 
}
步骤2:修改前端HTML代码,存储身份并传递参数

在你的student_form.html中加入以下逻辑:

<script>
// 读取模板注入的用户邮箱,存在前端全局变量
const CURRENT_USER_EMAIL = "<?= activeUserEmail ?>";

// 调用后端接口时主动传递邮箱参数
function loadFormData() {
  google.script.run
    .withSuccessHandler(renderForm) // 替换为你自己的成功回调函数
    .getData(CURRENT_USER_EMAIL);
}
</script>
步骤3:修改getData函数,接收前端传递的参数
// 新增参数接收前端传的用户邮箱
function getData(activeUserEmail) {
  console.log("getData User: " + activeUserEmail);
  try {
    if (activeUserEmail != "") {
      var userResults = teachersSheet.filter(function (row) { return row[0] == activeUserEmail; });
    } else {
      var userResults = "No User Email"
    }
    var userDepartment = userResults[0][2];

    if (userDepartment == "Admin") {
      var data = questionsSheet.getDisplayValues().filter(function (row) { return row[1] != "" && row[0] != "ID"; });
    }
    else {
      var data = questionsSheet.getDisplayValues().filter(function (row) { return row[3] == userDepartment && row[9] == "FALSE" && row[1] != ""; });
    };

    var data = [userResults, data];
    console.log(data);
    return data;
  } catch (e) {
    console.log(e);
  }
}

可选安全增强

如果担心前端篡改邮箱冒充其他用户,可以增加签名校验:

  1. 在脚本中定义一个仅你知道的密钥:const SECRET_KEY = "自定义的随机字符串"
  2. doPost时给邮箱生成HMAC签名,和邮箱一起注入模板
  3. 前端调用接口时同时传递邮箱和签名
  4. 后端校验签名合法性,确认邮箱未被篡改

其他可选方案

如果确实需要服务端临时存储用户身份,可以使用以下两种方案:

  • 带唯一标识的UserProperties存储:将用户邮箱作为key的一部分,比如user_email_${activeUserEmail},避免不同用户共用同一个key,需要定期清理过期的属性避免存储超限
  • CacheService缓存:同样用用户唯一标识作为key前缀,设置合理的过期时间(最长6小时),适合短期存储场景

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 18:54:03