首页 > 解决方案 > 如何在带有 sqlite 数据库的 nodejs 上使用 passport.js

问题描述

您好我对nodejs很陌生,这是我的第一个问题。我想将用户登录添加到我的快速服务器。所以我尝试了 passport.js 示例(express-4.x-local-example)。现在我尝试将用户存储在我的 sqlite 数据库中。我针对其他问题(使用 sqlite 的 node.js 护照认证)。但这并没有解决我的问题。我不想用它创建一个实时应用程序,我只想了解它是如何工作的。当我将用户添加到我的数据库并尝试登录时,我总是被定向到 /bad。

这是我写的代码:

数据库.js

const sqlite3 = require("sqlite3");
const sqlite = require("sqlite");
const fs = require("fs").promises;

async function provideDatabase() {
  const databaseExists = await fs
    .access("./.data/database.db")
    .catch(() => false);

  const db = await sqlite.open({
    filename: "./.data/database.db",
    driver: sqlite3.Database
  });

  if (databaseExists === false) {
   await db.exec(
      "CREATE TABLE users (id INTEGER PRIMARY KEY AUTOINCREMENT, username TEXT, password TEXT, salt TEXT)"
    );
  }

  return db;
}

module.exports = provideDatabase;

这是我的 server.js

var express = require('express');
var passport = require('passport');
var Strategy = require('passport-local').Strategy;
const provideDatabase = require("./database");
const database = provideDatabase();
const LocalStrategy = require('passport-local').Strategy
const bodyParser = require("body-parser");
var app = express();
app.use(bodyParser.json());
var crypto = require('crypto');




function hashPassword(password, salt) {
  var hash = crypto.createHash('sha256');
  hash.update(password);
  hash.update(salt);
  return hash.digest('hex');
}

passport.use(new LocalStrategy(async function(username, password, done) {
  const db = await database;
  db.get('SELECT salt FROM users WHERE username = ?', username, function(err, row) {
    if (!row) return done(null, false);
    var hash = hashPassword(password, row.salt);
    db.get('SELECT username, id FROM users WHERE username = ? AND password = ?', username, hash, function(err, row) {
      if (!row) return done(null, false);
      return done(null, row);
    });
  });
}));

passport.serializeUser(function(user, done) {
  return done(null, user.id);
});

passport.deserializeUser(async function(id, done) {
  const db = await database;
  db.get('SELECT id, username FROM users WHERE id = ?', id, function(err, row) {
    if (!row) return done(null, false);
    return done(null, row);
  });
});


app.post('/login', passport.authenticate('local', { successRedirect: '/good',
                                                    failureRedirect: '/bad' }));





// Configure view engine to render EJS templates.
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');




// Define routes.
app.get('/',
  function(req, res) {
    res.render('home', { user: req.user });
  });

app.get('/login',
  function(req, res){
    res.render('login');
  });
  

app.get('/logout',
  function(req, res){
    req.logout();
    res.redirect('/');
  });

app.get('/profile',
  require('connect-ensure-login').ensureLoggedIn(),
  function(req, res){
    res.render('profile', { user: req.user });
  });




  app.get("/user", async (request, response) => {
    const db = await database;
    const results = await db.all("SELECT * FROM users");
    response.send(results);
  });

app.post("/user", async (request, response) => {
  const db = await database;
  hashedPassword = hashPassword(request.body.password, request.body.salt)
  const created = await db.run(
    "INSERT INTO users (username, password, salt) VALUES(?,?,?)",
    request.body.username,
    hashedPassword,
    request.body.salt
  );

  const user = await db.get("SELECT * FROM users WHERE Id = ?", [
    created.lastID
  ]);
  response.status(201).send(user);
});


app.listen(3000);

标签: node.jssqliteexpresspassport.js

解决方案


我正在从生产项目中获取我的代码。虽然没有使用 SQLite,但概念应该是一样的。

我使用bcrypt而不是实现我自己的加密。我建议您也这样做,以避免在误用的情况下出现错误和安全问题。

我还将护照逻辑放在一个单独的文件中,以使代码看起来更清晰并避免混淆。

// Dependencies
const passport = require('passport');
const { Strategy: LocalStrategy, } = require('passport-local');
const bcrypt = require('bcrypt');

// Load model for User_DB
const { User_DB, } = require('../dataBase/dbConnection');

// Winston Logger
const passLog = require('../system/log').get('passportLog');

//  Session
// Take in user id => keep the session data small
passport.serializeUser((id, done) => {
    done(null, id);
});

// Deserialize when needed by querying the DB for full user details
passport.deserializeUser(async (id, done) => {
    try {

        const user = await User_DB.findById(id);
        done(null, user);
    } catch (err) {

        passLog.error(`Error Deserializing User: ${id}: ${err}`);
    }

});

// Export the passport module
module.exports = (passport) => {

    passport.use(new LocalStrategy({ usernameField: 'email', }, async (email, password, done) => {

        try {

            // Lookup the user 
            const userData = await User_DB.findOne({ email: email, }, { 
            password: 1, }); // Return the password hash only instead of the whole user object

            // If the user does not exist
            if (!userData) {

                return done(null, false);
            }

            // Hash the password and compare it to the hash in the database
            const passMatch = await bcrypt.compare(password, userData.password);

            // If the password hash does not match
            if (!passMatch) {
                return done(null, false);
            }

            // Otherwise return the user id
            return done(null, userData.id);

        } catch (err) {
            passLog.error(`Login Error: ${err}`);
        }

    }));
};

这些护照选项似乎经常出现故障或表现出奇怪的行为,因此我建议您像在我的控制器中一样处理重定向逻辑。

{ successRedirect: '/good',
 failureRedirect: '/bad' }

登录控制器逻辑:(我在这里省略了用于会话存储的代码并进行了一些修改,但此代码应该可以满足您的需要)

const login = (req, res, next) => {

    //Using passport-local
    passport.authenticate('local', async (err, user) => {


        //If user object does not exist => login failed
        if (!user) { return res.redirect('/unauthorized'); }


        //If all good, log the dude in
        req.logIn(user, (err) => {

            if (err) { return res.status(401).json({ msg: 'Login Error', }); }

            // Send response to the frontend
            return res.redirect('/good');
        });

      
        });
    })(req, res, next);


};

实际路线:

//  Import the controller
const {login} = require('../controllers/auth');

// Use it in the route
router.post('/auth/login', login);

推荐阅读