Express结合Passport登录报ERR_HTTP_HEADERS_SENT及Unauthorized问题
问题现象
通过Insomnia/Postman向登录接口提交JSON格式的用户名、密码数据时,接口返回Unauthorized,测试返回结果如下:
服务端控制台抛出ERR_HTTP_HEADERS_SENT: Cannot set headers after they are sent to the client错误。
补充说明:已补充Passport验证策略(Strategy)相关代码。
问题相关代码
passport.use( new Strategy(function (username, password, cb) { const isAdmin = (username === 'admin') && (password === adminPassword) if (isAdmin) cb(null, { username: 'admin' }) cb(null, false) }) ) passport.serializeUser((user, cb) => cb(null, user)) passport.deserializeUser((user, cb) => cb(null, user)) app.use( expressSession({ secret: sessionSecret, resave: false, saveUninitialized: false }) ) app.use(passport.initialize()) app.use(passport.session()) app.post('/login', passport.authenticate('local'), (req, res) => res.json({ success: true }) )
错误栈信息
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (_http_outgoing.js:485:11) at ServerResponse.header (G:\Work\node\01\node_modules\express\lib\response.js:794:10) at ServerResponse.json (G:\Work\node\01\node_modules\express\lib\response.js:275:10) at G:\Work\node\01\complete-servert\server-01.js:44:9 at Layer.handle [as handle_request] (G:\Work\node\01\node_modules\express\lib\router\layer.js:95:5) at next (G:\Work\node\01\node_modules\express\lib\router\route.js:144:13) at complete (G:\Work\node\01\node_modules\passport\lib\middleware\authenticate.js:271:13) at G:\Work\node\01\node_modules\passport\lib\middleware\authenticate.js:278:15) at pass (G:\Work\node\01\node_modules\passport\lib\authenticator.js:428:14) at Authenticator.transformAuthInfo (G:\Work\node\01\node_modules\passport\lib\authenticator.js:450:5) { code: 'ERR_HTTP_HEADERS_SENT' } Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client at ServerResponse.setHeader (_http_outgoing.js:485:11) at ServerResponse.header (G:\Work\node\01\node_modules\express\lib\response.js:794:10) at ServerResponse.json (G:\Work\node\01\node_modules\express\lib\response.js:275:10) at handleError (G:\Work\node\01\complete-servert\middleware.js:26:21) at Layer.handle_error (G:\Work\node\01\node_modules\express\lib\router\layer.js:71:5) at trim_prefix (G:\Work\node\01\node_modules\express\lib\router\index.js:326:13) at G:\Work\node\01\node_modules\express\lib\router\index.js:286:9) at Function.process_params (G:\Work\node\01\node_modules\express\lib\router\index.js:346:12) at next (G:\Work\node\01\node_modules\express\lib\router\index.js:280:10) at next (G:\Work\node\01\node_modules\express\lib\router\route.js:129:14)
根因分析
两个独立问题共同导致报错:
- Passport本地验证策略逻辑缺失
return语句:账号密码校验通过调用回调后,代码会继续向下执行校验失败的回调,连续两次向客户端返回响应,触发HTTP头已发送的错误 passport-local默认仅解析application/x-www-form-urlencoded格式的请求体,未添加JSON请求体解析中间件时,策略无法从JSON格式的请求中读取username、password字段,直接判定校验失败返回401 Unauthorized
修复方案
- 补全请求体解析中间件,在路由定义前添加如下代码,同时支持JSON和表单格式的请求参数:
app.use(express.json()) app.use(express.urlencoded({ extended: true }))
- 修正验证策略的回调逻辑,调用回调后添加return阻断后续代码执行,同时显式指定从请求体中读取用户名、密码字段:
passport.use( new Strategy({ usernameField: 'username', passwordField: 'password' }, function (username, password, cb) { const isAdmin = (username === 'admin') && (password === adminPassword) if (isAdmin) { return cb(null, { username: 'admin' }) } return cb(null, false) }) )
- (可选)自定义登录路由的响应逻辑,替换默认的passport authenticate包装,方便定位问题:
app.post('/login', (req, res, next) => { passport.authenticate('local', (err, user, info) => { if (err) return next(err) if (!user) return res.status(401).json({ success: false, message: '用户名或密码错误' }) req.logIn(user, (err) => { if (err) return next(err) return res.json({ success: true }) }) })(req, res, next) })
验证方式
重启服务后,在Insomnia中向/login接口发送POST请求:
- 请求头设置
Content-Type: application/json - 请求体传入正确格式的参数:
{"username": "admin", "password": "对应配置的管理员密码"}
即可正常返回登录成功响应,响应头中会携带会话Cookie。
内容的提问来源于stack exchange,提问作者Vivek K. Singh
相关产品推荐
相关产品推荐

