同一个接口昨天正常,今天统一返回 401。先不要重新创建一堆 Token;401 可能发生在 Magento 之前,也可能是 Token、Integration 状态、ACL 或 Store 路由问题。
建立一条可重复的最小请求
export API_BASE='https://www.example.com'
export API_TOKEN='在安全终端中设置,不要写进脚本仓库'
curl -sS -i -H "Authorization: Bearer ${API_TOKEN}" -H 'Accept: application/json' "${API_BASE}/rest/V1/store/storeConfigs"
保留 HTTP 状态、响应 body 和服务器 request ID。不要在工单里粘贴完整 curl,因为 shell 历史、CI 日志和截图都可能泄露 Token。
先看 401 是谁返回的
Magento JSON 常带 message 和参数;反向代理、WAF 或 Basic Auth 返回的 body/响应头不同。比较:
curl -sS -D /tmp/no-auth.headers -o /tmp/no-auth.body "${API_BASE}/rest/V1/store/storeConfigs"
curl -sS -D /tmp/token.headers -o /tmp/token.body -H "Authorization: Bearer ${API_TOKEN}" "${API_BASE}/rest/V1/store/storeConfigs"
diff -u /tmp/no-auth.headers /tmp/token.headers
两次响应完全相同,很可能 Authorization 在 CDN/Nginx/PHP-FPM 前丢失。检查 Nginx FastCGI 参数和代理规则是否转发该头,不要把 Token 打进访问日志。
用一个已知允许的端点区分 ACL
某端点 401/403、另一个成功,Token 本身可用,重点查 Integration 的 API 资源。后台进入 System → Integrations,打开对应 Integration,确认状态为 Active,并只勾选需要的资源。保存资源后,按版本和集成类型确认是否需要重新授权。
自定义接口的 webapi.xml 必须声明正确 resource:
<route url="/V1/alwayly/job/:id" method="GET">
<service class="AlwaylyJobApiJobRepositoryInterface"
method="getById"/>
<resources>
<resource ref="Alwayly_Job::job_read"/>
</resources>
</route>
同时在 acl.xml 中定义相同 ID。拼写不一致时,管理员全权限可能通过,Integration 却被拒绝。
检查 Store Code 路由
curl -sS -i -H "Authorization: Bearer ${API_TOKEN}" "${API_BASE}/rest/all/V1/store/storeConfigs"
curl -sS -i -H "Authorization: Bearer ${API_TOKEN}" "${API_BASE}/rest/default/V1/store/storeConfigs"
实际 Store code 不一定叫 default。路由被 Web Server 重写成另一个站点,或 Base URL 跳转到其他主机时,Authorization 可能在跨主机跳转中被丢弃。用 -i 看 Location,不要先加 -L 隐藏跳转。
OAuth 方式再查时钟和 nonce
如果不是 Bearer Token,而是 OAuth 1.0a,服务器时钟偏差、重复 nonce、签名 Base String 或 URL 编码都会导致认证失败。比较应用、Magento 节点与数据库的 UTC 时间,并保留一次脱敏签名输入用于重算。
date -u
mysql -Nse 'SELECT UTC_TIMESTAMP();'
修复后用最小只读端点、目标业务端点和一个无权端点各测一次:前两者成功,无权端点仍被拒绝,才能证明 Token 与 ACL 同时正确。

