4. 认证与授权失败:无效、过期、签名算法错误

认证与授权失败,应该是我们在集成SDK时最常遇到的一类问题了。我在项目中有个习惯,遇到这类报错第一反应不是改代码,而是先看请求里的几个关键字段——、、签名,十有八九问题就在这三个里面。

说白了,服务器不认识你,或者不信任你了。看起来就那么几种错误码,背后原因其实挺杂的。我们一个个捋清楚。

4.1 无效

,就是你的身份凭证。服务器通过它来识别你是哪个开发者、哪个应用。

常见的报错信息:

  • Invalid
  • not found
  • Authentication failed: rejected

我遇到过的原因主要有几种:

4.1.1 Key写错了或根本没填

这个看着简单,但犯的人真不少。有人直接把文档里的占位符YOUR_复制过去用了。还有人大小写写反了,或者在前面多了个空格。

嗯,这里要注意。有些SDK在初始化时会从配置文件读取Key,你得确认一下配置文件的路径对不对。我曾经在一个项目里折腾了半小时,最后发现是配置文件没被打包进资源目录。

4.1.2 Key的格式不对

不同平台的格式差异很大。有的是32位hex字符串,有的是JWT格式。有些SDK会做格式校验,格式不对直接拒绝。

我的做法:先把Key打印到日志里,肉眼看看长度和字符组成。

// 常见的格式检查
// 32位hex: 5f4dcc3b5aa765d61d8327deb882cf99
// Base64: dGhpc2lzYXRlc3RrZXk=
// JWT: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...

console.log(" length:", .length);
console.log("Prefix:", .substring(0, 8));

4.1.3 Key被禁用了或过期了

有些平台的有有效期,或者在控制台被手动停用了。你想想看,开发环境用的好好的,突然报Invalid ——我第一反应就是去后台刷新一下Key。

我的习惯: 每个项目都单独创建一个,不要多个项目混用。出了问题,禁用一个不影响其他项目。

4.2 过期

过期比无效更让人头疼。因为过期的时机不确定,很可能在用户正用着的时候突然失效。

为什么会这样?的设计初衷就是短时效。通常有效期在几分钟到几小时不等。超过时效,服务器就认为这个不再安全,直接拒绝。

报错信息一般是:

  • expired
  • Access invalid or expired
  • 401 Unauthorized

4.2.1 过期时间没算对

里有exp字段,存的是过期时间戳。我遇到过有人把秒级时间戳当毫秒级传,或者时区没对齐,导致刚生成就过期了。

排查方法:把解码出来看看。

// 以JWT为例,解码payload
function decodeToken() {
  const payload = .split('.')[1];
  const decoded = atob(payload);
  console.log(" payload:", decoded);
  // 看exp字段
}
关键点: 不要在前端解码JWT来验证有效期,放个假的exp字段骗不了服务器。解码只是为了排查问题。

4.2.2 刷新的逻辑没跑通

大多数SDK支持自动刷新,但刷新本身需要另一个Refresh 。Refresh 过期了,那自动刷新就失效了。用户就得重新登录。

我在项目中遇到过这样一个坑:刷新的接口返回了新的Access ,但没把新的Refresh 一起更新。下次再刷新时用的是旧的Refresh ,直接被拒。

正确的做法是:刷新响应里但凡有新,全部更新本地存储。

// 刷新的正确姿势
async function refreshTokens() {
  const response = await fetch('/auth/refresh', {
    method: 'POST',
    body: JSON.stringify({ refreshToken: storedRefreshToken })
  });
  const data = await response.json();

  // 更新本地的Access 和Refresh 
  localStorage.setItem('accessToken', data.accessToken);
  localStorage.setItem('refreshToken', data.refreshToken || storedRefreshToken);
}
核心原则: 每次刷新响应里的Access 和Refresh 都可能是新的,不能只更新其中一个。

4.3 签名算法错误

签名算法错误,是认证失败里最隐蔽的一类。和的问题,看日志基本能定位。但签名不对,服务端通常只回一句Signature verification failed,没有更多细节。

为什么需要签名?说白了,就是确保请求内容的完整性和真实性。和只是身份凭证,签名是给每个请求盖了个章,证明这个请求是你发的、没被篡改过。

4.3.1 签名字段没拼接对

签名前需要把请求参数按指定顺序拼接成一个字符串。顺序错了,结果就完全不一样。我见过有人把参数按字母序排,但文档要求按字段顺序排。

举个例子:

// 假设文档要求拼接顺序:method + uri + timestamp + body
// 错误的做法(参数顺序错乱)
const signStr = timestamp + method + uri + body;
// 正确的做法
const signStr = method + uri + timestamp + body;

4.3.2 签名算法选错了

不同SDK用的签名算法不一样。有的是HMAC-SHA256,有的是RSA-SHA256,还有的自定义算法。你如果用了HMAC,但服务端配的是RSA,那怎么签都对不上。

我建议:先确认SDK文档里写的是什么算法。然后在代码里显式指定,不要依赖默认值。

算法 特点 常见场景
HMAC-SHA256 对称签名,密钥不公开 + Secret做签名
RSA-SHA256 非对称签名,私钥签名公钥验证 OpenAPI、OAuth2
MD5 不可逆哈希,不推荐 老版本SDK

4.3.3 时间戳不一致

很多签名算法会把时间戳加入签名字符串,防止重放攻击。但客户端的时钟和服务端的时钟相差太大,签名就对不上。

你可能会问:差多久就失效?这个要看SDK的配置。一般是5分钟。超过这个时间窗口,就算签名算法对,服务器也会拒绝。

我遇到过客户在虚拟机上跑测试,时钟快了整整10分钟。每次请求都报签名失败,改完系统时间就好了。

排查技巧: 在请求里打印出时间戳,和服务端日志对比一下。差几秒在合理范围内,差几分钟以上基本都是客户端时钟问题。

4.4 总结排查思路

嗯,我们把三种情况排查的先后顺序理一下:

  1. 先看:有没有填、填没填对、有没有过期。
  2. 再看:有没有过期、刷新逻辑跑没跑对。
  3. 最后看签名:参数字段顺序对不对、算法选没选对、时间戳有没有偏差。

这个顺序是我踩坑踩出来的。的问题最快定位,次之,签名最花时间。所以先从快的来。

核心原则: 认证失败的问题,先确认身份凭证,再确认时效性,最后确认请求完整性。别一上来就去翻签名代码。
安全提醒: 不要把或Secret硬编码在客户端代码里。我之前审计过一个项目,直接写在JavaScript文件里,打包后任何人都能翻到。 正确做法是从服务端下发,或者用密钥管理服务。
认证与授权失败排查流程 发起API请求 1. 是否有效? 无效则直接拒绝 2. 是否过期? 过期则尝试刷新 3. 签名算法是否正确? 字段顺序、算法、时间戳 返回授权结果 常见报错 Invalid expired Signature verification failed 401 Unauthorized

上面这张图是我在培训新人时经常用的排查流程。从左到右看,先搞定,再搞定,最后搞签名。每步都有对应的报错和解决手段。

最后,做个总结。认证与授权失败,十有八九是这三类问题。先把和的时效性确认好,再去扣签名细节。签名排查时,记得先对齐时间戳,再检查拼接顺序。按照这个思路走,绝大多数问题都能在10分钟内定位出来。