Google Web Apps 获取用户邮箱同时以所有者身份运行脚本问题咨询
首选解决方案(最简实现)
首先纠正一个常见认知误区:只有当Web App的访问权限设置为「任何人,甚至匿名用户」时,部署为所有者运行才会拿不到访问用户的邮箱。只要你按以下配置部署,不需要做额外开发就能解决所有问题:
- Web App部署配置
- 执行身份:我(脚本所有者)
- 有权访问的人:拥有Google账号的用户(如果仅限内部使用可选「我的组织内的用户」)
- 核心代码逻辑
你不需要修改原有校验逻辑,因为脚本以所有者身份运行,访问存储权限的谷歌表格不需要给用户开通任何权限,同时可以正常通过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方案,完全解决安全问题:
- 核心思路
- 部署两个Web App,都放在同一个脚本项目下共享配置
- 前端App以用户身份运行,负责获取用户邮箱,生成带签名的请求
- 服务端App以所有者身份运行,部署为仅限所有者访问,负责校验请求合法性、处理权限查询等需要所有者权限的逻辑
- 具体实现步骤
首先在脚本项目的「项目设置」→「脚本属性」中添加一条配置:名称为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
相关产品推荐
相关产品推荐

