Node.JS/Passport-CI-OIDC - Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader

Node.JS/Passport-CI-OIDC - Error: Can't set headers after they are sent. at ServerResponse.OutgoingMessage.setHeader

我一直在尝试使用 passport-ci-oidc 和 node.js 执行一些身份验证。在以前的变体中,我使用了 passport-idaas-openidconnect 并且对我来说一切正常。现在我收到以下错误:

Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
    at ServerResponse.setHeader (_http_outgoing.js:470:11)
    at ServerResponse.header (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/response.js:718:10)
    at app.use (/Users/LSBTUser1/ibm/iERPedia/app.js:219:7)
    at Layer.handle [as handle_request] (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/layer.js:95:5)
    at trim_prefix (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/index.js:312:13)
    at /Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/index.js:280:7
    at Function.process_params (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/index.js:330:12)
    at next (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/index.js:271:10)
    at urlencodedParser (/Users/LSBTUser1/ibm/iERPedia/node_modules/body-parser/lib/types/urlencoded.js:91:7)
    at Layer.handle [as handle_request] (/Users/LSBTUser1/ibm/iERPedia/node_modules/express/lib/router/layer.js:95:5)

即使我将删除错误消息中提到的行 - 我也会收到同样的错误,但不会提及我的代码的任何地方(仅“/node_modules/serve-static/node_modules/send/index.js”中的错误)。我的代码:

function doAuth(req, res, next) {
  if (req.originalUrl.indexOf('/auth/sso/callback') === 0 || req.originalUrl === '/login') {    
    return next();
  }
  if (!req.isAuthenticated()) {
    callback_url = req.originalUrl;
    res.redirect('/login');
  }
  return next();
}

if (process.env.iERPediaAPIhost) {

  iERPediaAPIhost = process.env.iERPediaAPIhost;
  REQUEST_TIMEOUT = process.env.REQUEST_TIMEOUT ?
    Number(process.env.REQUEST_TIMEOUT) :
    REQUEST_TIMEOUT;
  FILE_SIZE_LIMIT = process.env.FILE_SIZE_LIMIT || FILE_SIZE_LIMIT;
  MAIL_URL = process.env.MAIL_HOST_URL || MAIL_URL;

  iERPediaAPIport = 80;

  config = {
    applicationId: appEnv.app.application_id,
    applicationRoute: appEnv.url,
  };

  // +++ START NEW BlueID ++++
  const Strategy = new OpenIDConnectStrategy({
    discoveryURL: process.env.BlueID_discovery_url,
    clientID: process.env.BlueID_client_id,
    scope: 'openid',
    response_type: 'code',
    clientSecret: process.env.BlueID_client_secret,
    callbackURL: process.env.BlueID_callback_url,
    skipUserProfile: true,
  },
    (iss, sub, profile, accessToken, refreshToken, params, done) => {
      process.nextTick(() => {
        profile.accessToken = accessToken;
        profile.refreshToken = refreshToken;
        done(null, profile);
      });
    });

  app.get('/login', passport.authenticate('openidconnect'));
  app.use(cookieParser());
  app.use(cookiesession({
    name: 'bp',
    secret: 'kurkawodna',
    cookie: { maxAge: 8 * 60 * 60 * 1000 }, // hours
  }));
  app.use(passport.initialize());
  app.use(passport.session());

  passport.use(Strategy);

  passport.serializeUser((user, done) => {
    user._json.blueGroups = [];
    done(null, user);
  });

  passport.deserializeUser((obj, done) => {
    done(null, obj);
  });
}

app.use('*', (request, response, next) => { doAuth(request, response, next); });

// serve the files out of ./public as our main files
app.use(express.static(path.join(__dirname, '/dist')));

app.use('/api', proxy(`${iERPediaAPIhost}:${iERPediaAPIport}`, {
  proxyReqOptDecorator: function proxyReqOptDecorator(proxyReq) {
    proxyReq.headers['X-ApplicationId'] = config.applicationId;
    return proxyReq;
  },
  reqBodyEncoding: null, // default is utf8. But with utf8 files are converted from binary to utf8.
  limit: FILE_SIZE_LIMIT,
}));

app.use('/mail', proxy(`${MAIL_URL}:443`, {
  proxyReqOptDecorator: function proxyReqOptDecorator(proxyReq) {
    proxyReq.headers['Authorization'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';
    proxyReq.headers['Content-Type'] = 'application/json';
    return proxyReq;
  },
  reqBodyEncoding: null, // default is utf8. But with utf8 files are converted from binary to utf8.
  limit: FILE_SIZE_LIMIT,
  https: true,
}));

app.use('/Bluepages', proxy(`${BLUEPAGES_URL}:${BLUEPAGES_PORT}`, {
  proxyReqOptDecorator: function(proxyReqOpts, originalReq) {
    proxyReqOpts.headers['X-IBM-Client-Id'] = 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx';
    proxyReqOpts.headers['X-IBM-Client-Secret'] = 'xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx';    
    proxyReqOpts.key = [bpKey];
    proxyReqOpts.cert = [bpCert];
    return proxyReqOpts;
  }
}));

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));

console.log('######################################################################');
console.log('# iERPedia three');
console.log(`# Uses connection to ${iERPediaAPIhost}:${iERPediaAPIport}`);
console.log('######################################################################');


function getUserDataFromBP(req) {
  return {
    id: req.user._json.uid,
    intranet_id: req.user.id,
    name: req.user._json.firstName,
    surname: req.user._json.lastName,
    email: req.user._json.emailAddress,
  };
}

function getSendGridAuth() {
  return {
    user: EMAIL_USER,
    api_key: EMAIL_API_KEY,
  };
}

function getApplicationUrl() {
  return { url: appEnv.url };
}

// enable CORS: http://enable-cors.org/server_expressjs.html
app.use((req, res, next) => {
  res.set('Access-Control-Allow-Origin', '*');
  res.set('Access-Control-Allow-Headers', 'Origin, X-Requested-With, Content-Type, Accept, Authorization');  
  next();
});


app.get('/auth/getCurrentUser', (req, res) => {
  res.send(getUserDataFromBP(req));
});

app.get('/auth/getSendGridAuth', (req, res) => {
  res.send(getSendGridAuth());
});

app.get('/getApplicationURL', (req, res) => {
  res.send(getApplicationUrl());
});

app.get('/logout', (req) => {
  req.logOut();
  req.session = null;
});

app.get('/auth/sso/callback', (req, res, next) => {
  passport.authenticate('openidconnect', {
    successRedirect: callback_url,
    failureRedirect: '/failure',
  })(req, res, next);
});

app.get('/failure', (req, res) => {
  res.send('Login failed. Please try again');
});

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'));
});

// start server on the specified port and binding host
const server = app.listen(appEnv.port, '0.0.0.0', () => {
  // print a message when the server starts listening
  console.log(`server starting on ${appEnv.url}`);
});

server.timeout = REQUEST_TIMEOUT; // 5 min for uploading file

这可能是doAuth中的重定向导致的。如果中间件没有呈现任何东西,你只应该调用 next() 。这应该修复:

function doAuth(req, res, next) {
  if (req.originalUrl.indexOf('/auth/sso/callback') === 0 || req.originalUrl === '/login') {    
    return next();
  }
  if (!req.isAuthenticated()) {
    callback_url = req.originalUrl;
    return res.redirect('/login');
  }
  return next();
}

还有两件事:

而不是使用 app.use('*', (request, response, next) => { doAuth(request, response, next); });

你可以简单地使用 app.use('*', doAuth);

并在文件开头设置cors和common headers(否则某些路由将没有cors)