Вопрос по connect, passport.js, node.js, express – Предотвратить Expressjs от создания сеанса, когда запросы содержат заголовок авторизации?

8

У меня есть API, который можно вызвать либо с помощью браузера, где запросы являются транзакционными и имеют сеанс ИЛИ напрямую, например. используя curl, где запросы являются атомарными. Запросы браузера должны сначала пройти аутентификацию, а затем использовать экспресс-сеанс (connect.sid) для последующей авторизации, прямые вызовы API используют заголовок:Authorization: "SOMETOKEN" который должен быть отправлен для каждого запроса.

У меня проблема в том, что яИспользуя один и тот же веб-сервер для обслуживания как атомарного, так и транзакционного трафика, каждый вызов API без необходимости получает сеанс Express. Каждый ответ включает в себя Set-Cookie, и все эти сеансы заполняют мое хранилище сеансов. Следовательно:Как я могу запретить Express вводить новый ключ sess в хранилище памяти (Redis), если запрос содержит заголовок авторизации?

Заметка. Я понимаю, что более классический подход состоит в том, чтобы иметь отдельный API-сервер и отдельный WEB-сервер, но почему бы не запустить оба на одной машине? Для меня разница в том, что API обслуживает данные, а WEB обслуживает представления, но помимо этого они оба являются частью одного и того же приложения. Я просто позволил пользователям получать доступ к своим данным напрямую и незаставить их использовать мой интерфейс.

Экспресс Конфиг
module.exports = function(app, exp, sessionStore, cookieParser, passport, flash) {

    app.configure(function(){
        // Templates
        app.set('views', ERNEST.root + '/server/views');
        app.set('view engine', 'jade');
        app.set('view options', { doctype : 'html', pretty : true });

        // Allow large files to be uploaded (default limit is 100mb)
        app.use(exp.limit('1000mb'));

        // Faux putting and deleting
        app.use(exp.methodOverride());

        // Static content
        app.use(exp.static(ERNEST.root + '/server'));
        app.use(exp.static(ERNEST.root + '/public'));

        // Handle favicon
        app.use(exp.favicon());

        // For uploads
        app.use(exp.bodyParser({keepExtensions: true}));

        // Configure cookie parsing
        if ( cookieParser ) app.use(cookieParser);
        else app.use(exp.cookieParser());

        // Where to store the session
        var session_options = { 'secret': "and she put them on the mantlepiece" };
        if ( sessionStore ) session_options.store = sessionStore;
        app.use(exp.session( session_options ));

        // Rememberance
        app.use( function (req, res, next) {
            if ( req.method == 'POST' && req.url == '/authenticate' ) {
                if ( req.body.rememberme === 'on' ) {
                    req.session.cookie.maxAge = 2592000000; // 30*24*60*60*1000 Rememeber 'me' for 30 days
                } else {
                    req.session.cookie.expires = false;
                }
            }
            next();
        });

        // PassportJS
        if ( passport ){
            app.use(flash());
            app.use(passport.initialize());
            app.use(passport.session());
        }
    });

};
Пример маршрута
app.get('/status/past_week', MID.ensureAuthenticated, MID.markStart, function(req, res) {
    WEB.getStatus('week', function(err, statuses){
        if ( err ) res.send(500, err);
        else res.send(200, statuses);
    });
});
Средство авторизации
MID.ensureAuthenticated = function(req, res, next) {
  if ( req.isAuthenticated() ) return next();
  else {
        isAuthorised(req, function(err, authorised){
            if ( err ) return res.redirect('/');
            else if ( authorised ) return next();
            else return res.redirect('/');
        });
    }

    function isAuthorised(req, callback){
        var authHeader = req.headers.authorization;
        if ( authHeader ) {
            // Has header, verify it
            var unencoded = new Buffer(authHeader, 'base64').toString();
            var formatted = unencoded.toString().trim();
            ACCOUNT.verifyAuth(formatted, callback); // verifyAuth callbacks next() when successful
        } else callback(null, false); // No Authorised header
    }
};

Ваш Ответ

4   ответа
-1

Вы всегда можете просто перехватить событие заголовка ответа и удалить'set-cookie' заголовок:

app.use(function(req, res, next) {
  res.on('header', function () {
    if (req.headers.authorization) {
      delete res._headers['set-cookie'];
    }
  });
  next();
});

Технически вы можете поместить это где угодно в вашей цепочке промежуточного программного обеспечения.

Используя этот подход проверки каждого запроса, один из вариантов заключается в использованииreq.session.destroy(); удалить сеанс сразу после того, как Express установил его. Я'Я бы предпочел, чтобы Express не установил его в первую очередь. Oliver Lloyd
Чтобы быть справедливым, тыМы прямо ответили на вопрос, но этот работа. Он успешно удаляет заголовок set-cookie, но этоЭто не моя настоящая проблема. Моя настоящая проблема - все ключи sess, создаваемые в моем хранилище памяти (Redis), по одному на запрос - это быстро пожирает память. Я'Я обновлю вопрос, чтобы отразить это. Oliver Lloyd
1

Похоже, вам нужно написать свое собственное промежуточное программное обеспечение для сессии.Вот'пример, Если вы можете создать отдельный поддомен, например, www.example.com для сеансов браузера и app.example.com для прямого доступа к нему, то вы сможете использовать связанный метод почти точно, и просто неt начать сеанс для запросов app.example.com. Это может быть самый прямой метод, при котором вызов указывает метод, которым он намеревается аутентифицироваться, и любое отклонение от этого является ошибкой.

Иначе тыВам придется обнаружить токен аутентификации в промежуточном программном обеспечении и не начинать сеанс, когда вы его найдете.

Спасибо, ваше предложение использовать отдельный поддомен имеет большой смысл; Я подозреваю, что в итоге мы это сделаем, и тогда ваша ссылка будет полезна. Oliver Lloyd
17

Попробуй это:

var sessionMiddleware = exp.session( session_options );

app.use(function(req, res, next) {
  if (req.headers.authorization) {
    return next();
  }
  return sessionMiddleware(req, res, next);
});
Спасибо. Это именно то, что я искал. Oliver Lloyd
1

Альтернативный подход к сокращению количества сессий, хранящихся в хранилище сессий, - установить для maxAge по умолчанию значение, меньшее. Затем, когда вам действительно нужно хранить сессии дольше, как после входа пользователя в систему, вы можете установитьreq.session.cookie.expires = null;, Также не надоне забудьте установить истечение сеанса на что-то низкое, когда пользователь выходит из системы.

Вот'Вот пример:

// set default to something low
app.use(session({
  resave: true,
  saveUninitialized: true,
  cookie: {
    maxAge: 5 * 60 * 1000 // 5 minutes
  },
  secret: secrets.sessionSecret,
  store: new MongoStore({
    url: yourUrl,
    auto_reconnect: true
  })
}));

// on successful login, 
// set expiration to null or something longer than default
var time = 14 * 24 * 3600000; //2 weeks
req.session.cookie.maxAge = time;
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();

// on logout, reset expiration to something low  
var time = 5 * 60 * 1000; // 5 minutes
req.session.cookie.maxAge = time; //2 weeks
req.session.cookie.expires = new Date(Date.now() + time);
req.session.touch();

Это особенно полезно при удаленном мониторинге вашего приложения, потому что, если мониторинг достаточно частый, сеансы заполнятся быстро.

Похожие вопросы