今天在给 ProfileKit 加「Save Profile」功能的时候踩了一个坑,绕了差不多半天才走出来,顺手记一篇。这个扩展做的事不复杂:从 LinkedIn 个人页里取姓名、头衔和当前公司,POST 到一个 Web App,再由它写进 Google Sheet 对应公司的那一行,Sheet 就当数据库用。Web App 是拿 Google Apps Script 部署的,省得为这个小项目单独起一台服务器。结果代码写好了,一同步就报 HTTP 401,而且断点打不进去、日志里什么都没有——请求在入口就被拦了,根本没走到我的代码。
一开始根本没往部署配置那边想。先怀疑是 CORS 拦截,又觉得可能是 URL 拼错了,甚至把 Apps Script 的代码从头翻了一遍。后来点开改动历史,看到之前部署生成的 manifest,才意识到 UI 上那个「Anyone」和底层值可能不是一回事。
拆开看看这一步。新版部署界面里有一栏「谁可以访问」,我选的那个选项在界面上显示成「Anyone」。光看这个标签,理解为任何人都能调是正常的。但它对应的 manifest 值是 ANYONE,在 Google 的 API 语义里是「任何已登录 Google 账户的用户」,不是匿名公开访问。浏览器扩展里的 fetch 一般是匿名的,不带任何 Google 账户凭证,所以每次请求都被当成未认证弹了回来。
实际能用的是三个值:OWNER 只有部署者自己能调,ANYONE 需要登录 Google 账户,ANYONE_ANONYMOUS 才是完全不登录也能调。界面把后面两个合并显示成了同一个词「Anyone」,登录这个前提被藏了起来,两边的差别就不见了。
要修的话,把 manifest 里 webapp 的 access 字段改成 ANYONE_ANONYMOUS,executeAs 保持 USER_DEPLOYING——脚本仍然以我的身份去读写 Google Sheet,调用方不需要登录。改好之后的 webapp 配置长这样:
{
"webapp": {
"executeAs": "USER_DEPLOYING",
"access": "ANYONE_ANONYMOUS"
}
}
更新好之后我用 curl 直接打了一发不带任何认证的 POST,看到返回 200,才确认这次真的修好了。用 -i 是为了把响应状态码一起打出来:
curl -i -X POST https://script.google.com/macros/s/AKfycb.../exec
💡 这个坑记下来是因为它和代码没关系,纯粹是部署配置的问题,UI 还把最关键的差异藏掉了。以后如果再遇到 Web App 返回 401,我不会先去翻业务代码,而是先用 curl 打一发匿名请求:返回 401,基本可以断定是访问权限设错了;返回 200,再回头查逻辑。这个方向能把认证问题和业务问题分成两半来查,哪一半出问题都好定位。
划重点:第一,部署配置里界面显示的「Anyone」和 manifest 的 ANYONE 语义不一样,匿名调用要用 ANYONE_ANONYMOUS;第二,改完配置一定要用无认证请求验证一次,不要假设它对;第三,遇到 401 先查部署配置,再回头查代码。这篇就记到这里,你可以拿 curl 试一下自己已有的 Apps Script 端点,如果看到 401,先检查 access 字段是不是 ANYONE_ANONYMOUS。
