javascript Express-session在重定向后不持久

o8x7eapl  于 2023-05-16  发布在  Java
关注(0)|答案(5)|浏览(131)

昨天,我试着用Node.js和Express.js框架做了一个网站。在这个网站上,用户需要登录,与凭证,谁在数据库中检查。如果凭证是正确的,我想保存用户的信息在一个会话。
在这个目标中,我使用中间件Express-Session,但我有一个问题。当用户键入正确的凭据时,其信息将正确存储在会话中。但是,在将用户重定向到主页后,会话被清除,因此存储用户信息的变量现在未定义。
我尝试了很多解决方案,我搜索了很多,但我没有达到解决这个问题...
这是我的代码:app.js:

const createError = require('http-errors');
const express = require('express');
const session = require('express-session');
const path = require('path');
const helmet = require('helmet');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
const bodyParser = require('body-parser');

const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
const admRouter = require('./routes/adm');

const urlencodedParser = bodyParser.urlencoded({extended : false});

const app = express();

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'ejs');

app.use(logger('dev'));
app.use(express.json());
app.use(urlencodedParser);
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({secret: 'secret', resave: false, saveUninitialized: false, cookie: { maxAge : 60000, secure: false }}));

app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use('/adm', admRouter);

// On utilise helmet pour sécuriser l'application.
app.use(helmet());

// catch 404 and forward to error handler
app.use(function(req, res, next) {
    next(createError(404));
});

// error handler
app.use(function(err, req, res, next) {
    // set locals, only providing error in development
    res.locals.message = err.message;
    res.locals.error = req.app.get('env') === 'development' ? err : {};

    // render the error page
    res.status(err.status || 500);
    res.render('error');
});

module.exports = app;

app.listen(80);

和index.js:

const express = require('express');
const router = express.Router();
const bodyParser = require('body-parser');
const verif = require('../functions/verif');
const password = require('node-php-password');

const dbServer = require('../database');
// const credentials = require('../functions/dbCredentials');

// const dbServer = mysql.createConnection(credentials);

const urlencodedParser = bodyParser.urlencoded({extended : false});

/* GET home page. */
router.get('/', function(req, res, next) {
    console.log(JSON.stringify(req.session.user));
    res.render('index', {verif, req });
});

router.post('/login', urlencodedParser, (req, res, next) => {
    if(req.body.username !== undefined && req.body.password !== undefined) {
        if(req.body.username !== null && req.body.password !== null) {
            dbServer.query('SELECT * FROM users WHERE username = ?', [req.body.username], function(error, result, fields) {
                if (error) throw error;
                // if(password.verify(req.body.password, result))
                console.log("resultat : " + JSON.stringify(result));
                if(result.length > 0) {
                    const utilisateur = result[0]; // On stocke la ligne concernant l'utilisateur dans une constante locale.
                    console.log("L'utilisateur existe.");
                    // On teste le résultat obtenu, pour savoir si son mot de passe est correct.
                    if(password.verify(req.body.password, utilisateur.password)) {
                        console.log("Mot de passe correct.");
                        req.session.user = utilisateur;
                        console.log(req.session.user);
                    } else {
                        // TODO : Session, pour afficher l'erreur.
                        console.log("Mot de passe incorrect.");
                    }
                }
                else {
                    console.log("L'utilisateur n'existe pas.")
                    // TODO : Session, pour afficher l'erreur.
                }
            });
        }
    }
    res.redirect('/');
});

module.exports = router;

使用此代码,当用户登录时,将:

console.log(req.session.user);

显示正确的信息,但行:

console.log(JSON.stringify(req.session.user));

对于路由'/'显示“undefined”。
所以,我在这种情况下有点迷失…你有解决这个问题的办法吗?Thanks by advance =)

u7up0aaq

u7up0aaq1#

问题是,你过早地将它们重定向。数据库查询比在服务器上运行的大多数代码花费的时间要长得多,这就是为什么要使用回调和promise。我会将res.redirect('/')移到数据库查询的末尾。就在最后一个else语句之后。这应该可以修复它,因为它允许在重定向之前运行所有内容。

t9eec4r0

t9eec4r02#

我遇到过这个问题,并通过express-session自述文件给出的提示解决了它。
如果您查看www.example.com的https://github.com/expressjs/session#cookiesecure部分readme.md,则会注意到以下声明:
请注意安全:true是推荐选项。但是,它需要启用HTTPS的网站,即HTTPS对于安全Cookie是必要的。如果设置了secure,并且您通过HTTP访问站点,则不会设置cookie。
因此,我尝试了以下操作:

const sess = {
    secret: 'My app',
    store: MongoStore.create({ 
        mongoUrl: 'mongodb://localhost:27017/myBaseApp',
        crypto: {
            secret: 'My database'
        }
    }),
    resave: true,
    saveUninitialized: false,
    cookie: { 
        secure: false,            //setting this false for http connections
        maxAge: 3600000,
        expires: new Date(Date.now() + 3600000) 
    }
}

重定向开始正常工作。

yrwegjxp

yrwegjxp3#

您正在为express-session使用内存存储中的隐式。他们建议不要在生产环境中使用它,但我的经验是,即使在简单的开发环境中,它也非常不稳定。我尝试在超时setTimeout(() => res.redirect('/'), 1000)中重定向,从统计上看,它失败的情况比我直接调用它的情况要少,尽管这远非理想。
在我目前的情况下,我最终实现了自己的哑内存存储,只是为了向前推进,稍后将切换到数据库后端。

后期编辑:我做了一些广泛的测试,似乎在我的情况下,设置会话cookie和重定向之间可能存在一些竞争条件,我认为有时浏览器在设置sid之前会重定向。我通过在我的操作过程中记录每一步的活动会话来发现这一点,似乎新的会话正在毫无理由地在重定向上创建。

z9gpfhce

z9gpfhce4#

几乎所有有回调的东西都是异步的,这需要你的脚本在发送信息/继续其他可能创建所谓的竞争条件的行为之前等待回复。
正如其他答案所建议的,这里有一个如何在成功/失败时重定向的示例:

router.post('/login', urlencodedParser, (req, res) => {
    // only in some cases
    let sendRedirectDirectly = true;

    if(req.body.username !== undefined && req.body.password !== undefined) {
        if(req.body.username !== null && req.body.password !== null) {

            // here you need to wait for the result of the query 
            sendRedirectDirectly = false; 

            dbServer.query('SELECT * FROM users WHERE username = ?', [req.body.username], function(error, result, fields) {
                if (error) throw error;
                // if(password.verify(req.body.password, result))
                console.log("resultat : " + JSON.stringify(result));
                if(result.length > 0) {
                    const utilisateur = result[0]; // On stocke la ligne concernant l'utilisateur dans une constante locale.
                    console.log("L'utilisateur existe.");
                    // On teste le résultat obtenu, pour savoir si son mot de passe est correct.
                    if(password.verify(req.body.password, utilisateur.password)) {
                        console.log("Mot de passe correct.");
                        req.session.user = utilisateur;
                        console.log(req.session.user);

                        // now we need to call next() or forward the user again
                        res.redirect('/?loginSuccess');
                    } else {
                        // TODO : Session, pour afficher l'erreur.
                        console.log("Mot de passe incorrect.");

                        // if password is incorrect
                        res.redirect('/?incorrectPass');
                    }
                }
                else {
                    console.log("L'utilisateur n'existe pas.")
                    // TODO : Session, pour afficher l'erreur.

                    // if username not found:
                    res.redirect('/?incorrectCredentials');
                }
            });
        }
    }

    // as noted above
    if (sendRedirectDirectly) 
       res.redirect('/');
});

为了更好地理解,一些包有更好的async支持,并允许编写如下例所示的代码。(例如mssql包)
async / await函数/语句暂停脚本的执行,直到Promise you await被满足,并抛出可捕获的错误,这些错误可以由try/catch-block处理。
如果一个函数被声明为async,它总是返回一个Promise
你也可以把mysql-query封装成一个Promise:

function mysqlQuery(...args) {
   return new Promise((resolve, reject) => {
      dbServer.query(...args, (error, result, fields) => {
         if (error)
           return reject(error);
         resolve([result, fields]);
      })
   })
}

现在您可以用途:

router.post('/login', urlencodedParser, async (req, res) => {
// add some checks for null/undefined
  try {
    let [ result, fields ] = await mysqlQuery('SELECT * FROM users WHERE username = ?', [req.body.username]);
    if (result.length) {
      // test the password?
    } else {
      // invalid username
    }
  } catch (e) {
    console.error(e);
  }
  // with async behavior you can send it always at the end
  res.redirect('/');
})
lskq00tm

lskq00tm5#

为了克服非https主机中的 secure cookie问题,可以使用一些环境变量来设置安全cookie配置,即NODE_ENV

app.use(session({
  secret: SESSION_KEY,
  resave: false,
  saveUninitialized: true,
  proxy: NODE_ENV !== "development",
  cookie: { secure: NODE_ENV !== "development" }
}))

相关问题