You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Express结合Passport登录报ERR_HTTP_HEADERS_SENT及Unauthorized问题

Passport登录接口返回Unauthorized及响应头重复发送错误修复

问题现象

通过Insomnia/Postman向登录接口提交JSON格式的用户名、密码数据时,接口返回Unauthorized,测试返回结果如下:
insomnia output
服务端控制台抛出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

修复方案

  1. 补全请求体解析中间件,在路由定义前添加如下代码,同时支持JSON和表单格式的请求参数:
app.use(express.json())
app.use(express.urlencoded({ extended: true }))
  1. 修正验证策略的回调逻辑,调用回调后添加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)
    })
)
  1. (可选)自定义登录路由的响应逻辑,替换默认的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 15:06:21