错误信息
jwt must be provided
jsonwebtoken (Node.js) — JsonWebTokenError from jwt.verify()
含义
`jwt.verify()` 根本没有收到令牌:第一个参数是 `undefined`、`null` 或空字符串。jsonwebtoken 在解析之前就做了这项检查,所以此时密钥和令牌格式都还无关紧要。问题出在上游——从请求中提取令牌的代码什么也没找到。
原因
客户端没有发送 Authorization 头
常见请求到达路由时没有携带令牌——用户未登录、客户端忘了附加该请求头,或者令牌在客户端过期后被清除了。这时 `req.headers.authorization.split(" ")[1]` 这样的代码就会得到 `undefined`。
从错误的位置读取令牌
常见属性名拼写错误(`authorisation`,或在已转为小写的请求头映射上使用 `Authorization`)、`x-access-token` 这样的自定义请求头,或者代码并不读取的查询参数。Node 会把收到的请求头名称转为小写,因此 `req.headers.Authorization` 始终是 `undefined`。
会话 cookie 没有随请求发送
偶见跨源请求不会携带 cookie,除非客户端设置了 `credentials: "include"`,且 cookie 为 `SameSite=None; Secure`。没有 cookie-parser 中间件时,`req.cookies` 也是空的。
令牌还没准备好
偶见在登录流程保存令牌之前就开始获取数据的前端,或者异步从存储中读取令牌的前端,发出的前几个请求携带的是空值。
修复方法
- 1.
防御性地提取令牌,并返回 401
在调用 `verify` 之前检查令牌是否缺失,缺失时返回带 `WWW-Authenticate: Bearer` 头的 401。这样客户端看到的是“未经身份验证”,而不是未捕获错误导致的 500。
js function bearerToken(req) { const [scheme, token] = (req.headers.authorization ?? "").split(" "); return scheme?.toLowerCase() === "bearer" && token ? token : null; } app.use((req, res, next) => { const token = bearerToken(req); if (!token) return res.status(401).set("WWW-Authenticate", "Bearer").end(); try { req.user = jwt.verify(token, publicKey, { algorithms: ["ES256"] }); next(); } catch { res.status(401).set("WWW-Authenticate", 'Bearer error="invalid_token"').end(); } }); - 2.
让客户端附上令牌
在浏览器的网络面板中确认请求携带了 `Authorization: Bearer …`(或该 cookie)。如果在浏览器中能看到这个请求头,服务器上却没有,说明中间的代理把它去掉了。
js // Browser / Node 18+ fetch: the token must be attached explicitly. await fetch("https://api.example.com/orders", { headers: { Authorization: "Bearer " + accessToken }, }); // Cookies cross origins only with credentials and SameSite=None; Secure. await fetch("https://api.example.com/orders", { credentials: "include" });