从Google Sheets运行Google Drive中Apps Script遇权限问题求助
问题背景
我有一个Google Sheets工作簿,点击工作表上的「按钮」(方形形状)就能根据用户输入生成报告。现在要把这个工作簿分享给团队,成员需要复制工作簿来生成多份报告。同时我希望后续修改代码时,不用让团队重新下载最新版本,所以尝试把Apps Script文件存在公司共享Google Drive里,用工作簿内的脚本加载这个远程文件。
本地加载脚本代码
function getApp(){ var folderId = "<folder_id>"; var fileName = "<file_name>"; var scriptId = "<script_id>"; var folder = DriveApp.getFolderById(folderId); var files = folder.getFilesByName(fileName); var url = "https://script.google.com/feeds/download/export?id=" + scriptId + "&format=json" var options = { "method": "GET", "headers": { "Authorization": "Bearer " + ScriptApp.getOAuthToken() }, "muteHttpExceptions": true }; var response = UrlFetchApp.fetch(url, options); dataContentAsString = response.getContentText(); fileContents = JSON.parse(dataContentAsString); var codeFile = fileContents.files[1]; if (codeFile){ var code = codeFile.source; eval(code); buildReport(); } }
问题现象
该函数会获取索引1处的文件(包含脚本所有函数的对象)并运行buildReport函数。通过Logger.log(fileContents)能看到完整脚本,说明从Google Drive获取文件的操作正常。但运行时出现如下OAuth权限错误:
Exception: You do not have permission to call
SpreadsheetApp.getActive. Required permissions:
(spreadsheets.currentonly 权限 || spreadsheets 权限)
核心疑问
- 这是否意味着虽然能获取远程脚本文件,但该文件无法访问运行宏的工作表(包含脚本需根据用户初始输入操作的模板)?
- 这是实现我需求的最佳方式吗?
- 更新:我给工作簿添加了触发器,打开电子表格时能正常运行
buildReport(非预期行为,但至少能运行),但点击「生成报告」按钮时仍报错。为何本地脚本在触发器下能成功导入远程脚本并运行buildReport,点击按钮却不行?
解答
问题1
是的,本质是权限上下文不匹配。通过eval加载的远程脚本没有独立权限,完全继承执行eval的本地脚本的权限上下文。按钮触发场景下,本地脚本属于「简单触发器」,默认没有被授予操作当前工作表的权限,因此远程脚本调用SpreadsheetApp.getActive()时会触发权限报错。简单说,远程脚本本身没问题,但它依赖的本地脚本在按钮触发时没有足够权限访问工作表。
问题2
这不是最佳实现方式,核心缺陷包括:
- 权限不稳定:不同触发场景(按钮/触发器/手动运行)的权限上下文差异大,容易出现权限报错。
- 调试难度高:
eval加载的代码无法直接在脚本编辑器中调试,日志追踪也很麻烦。 - 安全风险:共享Drive中的脚本若被意外修改,所有使用模板的用户都会执行修改后的代码,存在不可控风险。
更推荐的替代方案:
- Apps Script库:将核心代码发布为库,让团队复制的工作簿引用该库。后续更新库版本后,成员只需在自己的工作簿中切换到最新库版本即可,无需重新复制模板。
- 绑定脚本版本控制:把核心代码放在工作簿的绑定脚本中,用Git管理版本。更新时发布脚本的新版本,用户可通过脚本编辑器的「版本历史」拉取最新代码,操作成本远低于重新复制模板。
问题3
这是因为触发器和按钮触发的权限机制完全不同:
- 打开电子表格的触发器属于「可安装触发器」,创建时你已经手动授予了脚本所需的所有权限(包括访问当前工作表的权限)。当触发器执行
getApp()时,本地脚本的权限上下文包含了操作工作表的权限,eval加载的远程脚本自然继承该权限,能成功调用SpreadsheetApp.getActive()。 - 按钮触发属于「简单触发器」,它的权限是受限的,默认只能执行无需授权的操作,且不会触发授权提示。即便你之前给脚本授权过,按钮触发场景也不会复用该授权上下文,因此远程脚本调用需要授权的工作表操作时会报错。
内容的提问来源于stack exchange,提问作者ML_Engine

