如何在Node.js中实现登录验证

78

我有一个运行中的节点服务器:

var server=http.createServer(function(request, responsehttp) {
    if (request.method == 'POST') {
        var body = '';
        request.on('data', function (data) {
            body += data;
        });
        request.on('end', function () {
            var POST = qs.parse(body);
            processquery(POST, request, responsehttp);
        });
    } else {
        var url_parts = url.parse(request.url, true);
        var query = url_parts.query;
        console.log(query);
        processquery(query, request, responsehttp);
    }
});

我希望为这个服务器添加登录表单。当用户经过身份验证后,它将被显示出来。
   function processquery(query, request, responsehttp){
    var returnResult = function (data){
        responsehttp.end(JSON.stringify(data));
    };

    if (!query.command) {
        fileprocess(request, responsehttp);
    }
    responsehttp.writeHead(200, {"Content-Type": "application/json"});
    switch(query.command) {
        case 'logout':
            logout(query, returnResult);
            break;
        case 'login':
            login(query, returnResult);
            break;
    }    
}

如果客户端没有给出任何命令,则正在处理查询函数将文件返回给客户端, 因此,我可以从客户端向服务器发送登录命令,但是当服务器收到带有用户名密码的登录命令时,它应该如何处理登录请求并返回登录成功或失败,为了编写这一部分,我需要帮助。

我尝试过的方法。

function login(request, callback) {
    if(request.username==users[request.username] && request.password==users[request.username].password) {
        users[request.username].auth=true;
        var data = {result:'success','message':'login successful'};
        callback(data);
    } else {
        var data = {result:'error','message':'login incorrect'};
        callback(data);
    }
}

请建议我如何在这里添加会话,我尝试将request变量添加到登录函数中,并尝试设置request.session变量,但它说request.session未定义。

请建议我如何编写此登录模块,以便可以为每个用户正确地维护登录身份验证。


连接已完成:http://blog.nodejitsu.com/sessions-and-cookies-in-node - XMen
7个回答

251

以下是我使用Express.js实现的方法:

1)检查用户是否经过身份验证:我有一个名为CheckAuth的中间件函数,我在每个需要用户经过身份验证的路由上使用它:

function checkAuth(req, res, next) {
  if (!req.session.user_id) {
    res.send('You are not authorized to view this page');
  } else {
    next();
  }
}

我在我的路由中像这样使用这个函数:

app.get('/my_secret_page', checkAuth, function (req, res) {
  res.send('if you are viewing this page it means you are logged in');
});

2) 登录路由:

app.post('/login', function (req, res) {
  var post = req.body;
  if (post.user === 'john' && post.password === 'johnspassword') {
    req.session.user_id = johns_user_id_here;
    res.redirect('/my_secret_page');
  } else {
    res.send('Bad user/pass');
  }
});

3)注销路由:

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

如果你想学习更多关于Express.js的知识,请查看他们的网站:expressjs.com/en/guide/routing.html 如果需要更复杂的内容,可以查看everyauth(它有很多可用的身份验证方法,如Facebook、Twitter等;此处有一个良好的教程:here)。


1
我认为你的 delete res.session.user_id; 应该改为 delete req.session.user_id; - Johan Kool
41
这是一个很好的基础解决方案。在进行身份验证时,有一件事情我喜欢添加,就是通过编辑响应头明确告诉浏览器不要缓存“受限制”的页面。在这种情况下,在 checkAuth 方法中调用 next() 之前,我会添加 res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); - thauburger
3
这可以防止用户访问受限页面、退出登录,然后使用返回按钮再次访问受限页面。更改缓存策略会强制重新呈现页面,并使“checkAuth”方法失败。 - thauburger
1
id 应该是 John 用户名的唯一标识符,例如数据库中存储为 ID 的 UUID。 - alessioalex
4
您可以使用Node bcrypt进行密码哈希:https://github.com/ncb000gt/node.bcrypt.js/ - alessioalex
显示剩余14条评论

6

实际上这并不是问题的真正答案,但这是更好的方法。

我建议您使用connect/express作为http服务器,因为它们可以节省大量时间。显然您不想重复造轮子。在您的情况下,使用connect/express会使会话管理变得更加容易。

此外,对于身份验证,我建议您使用everyauth。它支持许多身份验证策略,非常适合快速开发。

所有这些都可以通过从其文档中复制粘贴来轻松完成!


4

补充Farid的伪答案,

考虑使用Passport.js而不是everyauth

这个问题的答案提供了一些差异的见解。


将用户身份验证转移到Google、Facebook或其他网站上有很多好处。如果您的应用程序要求允许使用Passport作为唯一的身份验证提供者或与传统的登录方式一起使用,这会让用户体验更加轻松。


3

@alessioalex 的回答对于新手来说是一个完美的演示。但无论如何,把 checkAuth 中间件写到除了登录以外的所有路由中都很困难,因此最好将 checkAuth 从每个路由中移动到一个入口(entry)中,并使用 app.use。例如:

function checkAuth(req, res, next) {
  // if logined or it's login request, then go next route
  if (isLogin || (req.path === '/login' && req.method === 'POST')) {
    next()
  } else {
    res.send('Not logged in yet.')
  }
}

app.use('/', checkAuth)

1
我尝试了这个答案,但对我没有用。我也是一个 Web 开发的新手,在课程中使用过 mlab,但更喜欢 parse,所以我不得不寻找最合适的解决方案。这是我目前使用 parse 在 expressJS 上的解决方案。
1)检查用户是否已经通过身份验证:我有一个名为 isLogginIn 的中间件函数,我在需要用户进行身份验证的每个路由上使用它:
 function isLoggedIn(req, res, next) {
 var currentUser = Parse.User.current();
 if (currentUser) {
     next()
 } else {
     res.send("you are not authorised");
 }
}

我在我的路由中像这样使用这个函数:

  app.get('/my_secret_page', isLoggedIn, function (req, res) 
  {
    res.send('if you are viewing this page it means you are logged in');
  });

2) 登录路由:

  // handling login logic
  app.post('/login', function(req, res) {
  Parse.User.enableUnsafeCurrentUser();
  Parse.User.logIn(req.body.username, req.body.password).then(function(user) {
    res.redirect('/books');
  }, function(error) {
    res.render('login', { flash: error.message });
  });
});

3) 退出路由:

 // logic route
  app.get("/logout", function(req, res){
   Parse.User.logOut().then(() => {
    var currentUser = Parse.User.current();  // this will now be null
    });
        res.redirect('/login');
   });

这对我非常有效,我完全参考了这里的文档https://docs.parseplatform.org/js/guide/#users

感谢@alessioalex的回答。我只是更新了最新的实践。


1

======authorization====== MIDDLEWARE

const jwt = require('../helpers/jwt')
const User = require('../models/user')

module.exports = {
  authentication: function(req, res, next) {
    try {
      const user = jwt.verifyToken(req.headers.token, process.env.JWT_KEY)
      User.findOne({ email: user.email }).then(result => {
        if (result) {
          req.body.user = result
          req.params.user = result
          next()
        } else {
          throw new Error('User not found')
        }
      })
    } catch (error) {
      console.log('langsung dia masuk sini')

      next(error)
    }
  },

  adminOnly: function(req, res, next) {
    let loginUser = req.body.user
    if (loginUser && loginUser.role === 'admin') {
      next()
    } else {
      next(new Error('Not Authorized'))
    }
  }
}

====error handler==== MIDDLEWARE
const errorHelper = require('../helpers/errorHandling')

module.exports = function(err, req, res, next) {
  //   console.log(err)
  let errorToSend = errorHelper(err)
  // console.log(errorToSend)
  res.status(errorToSend.statusCode).json(errorToSend)
}


====error handling==== HELPER
var nodeError = ["Error","EvalError","InternalError","RangeError","ReferenceError","SyntaxError","TypeError","URIError"]
var mongooseError = ["MongooseError","DisconnectedError","DivergentArrayError","MissingSchemaError","DocumentNotFoundError","MissingSchemaError","ObjectExpectedError","ObjectParameterError","OverwriteModelError","ParallelSaveError","StrictModeError","VersionError"]
var mongooseErrorFromClient = ["CastError","ValidatorError","ValidationError"];
var jwtError = ["TokenExpiredError","JsonWebTokenError","NotBeforeError"]

function nodeErrorMessage(message){
    switch(message){
        case "Token is undefined":{
            return 403;
        }
        case "User not found":{
            return 403;
        }
        case "Not Authorized":{
            return 401;
        }
        case "Email is Invalid!":{
            return 400;
        }
        case "Password is Invalid!":{
            return 400;
        }
        case "Incorrect password for register as admin":{
            return 400;
        }
        case "Item id not found":{
            return 400;
        }
        case "Email or Password is invalid": {
            return 400
        }
        default :{
            return 500;
        }
    }
}

module.exports = function(errorObject){
    // console.log("===ERROR OBJECT===")
    // console.log(errorObject)
    // console.log("===ERROR STACK===")
    // console.log(errorObject.stack);

    let statusCode = 500;  
    let returnObj = {
        error : errorObject
    }
    if(jwtError.includes(errorObject.name)){
        statusCode = 403;
        returnObj.message = "Token is Invalid"
        returnObj.source = "jwt"
    }
    else if(nodeError.includes(errorObject.name)){
        returnObj.error = JSON.parse(JSON.stringify(errorObject, ["message", "arguments", "type", "name"]))
        returnObj.source = "node";
        statusCode = nodeErrorMessage(errorObject.message);
        returnObj.message = errorObject.message;
    }else if(mongooseError.includes(errorObject.name)){
        returnObj.source = "database"
        returnObj.message = "Error from server"
    }else if(mongooseErrorFromClient.includes(errorObject.name)){
        returnObj.source = "database";
        errorObject.message ? returnObj.message = errorObject.message : returnObj.message = "Bad Request"
        statusCode = 400;
    }else{
        returnObj.source = "unknown error";
        returnObj.message = "Something error";
    }
    returnObj.statusCode = statusCode;
    
    return returnObj;


}


===jwt====
const jwt = require('jsonwebtoken')

function generateToken(payload) {
    let token = jwt.sign(payload, process.env.JWT_KEY)
    return token
}

function verifyToken(token) {
    let payload = jwt.verify(token, process.env.JWT_KEY)
    return payload
}

module.exports = {
    generateToken, verifyToken
}

===router index===
const express = require('express')
const router = express.Router()

// router.get('/', )
router.use('/users', require('./users'))
router.use('/products', require('./product'))
router.use('/transactions', require('./transaction'))

module.exports = router

====router user ====
const express = require('express')
const router = express.Router()
const User = require('../controllers/userController')
const auth = require('../middlewares/auth')

/* GET users listing. */
router.post('/register', User.register)
router.post('/login', User.login)
router.get('/', auth.authentication, User.getUser)
router.post('/logout', auth.authentication, User.logout)
module.exports = router


====app====
require('dotenv').config()
const express = require('express')
const cookieParser = require('cookie-parser')
const logger = require('morgan')
const cors = require('cors')
const indexRouter = require('./routes/index')
const errorHandler = require('./middlewares/errorHandler')
const mongoose = require('mongoose')
const app = express()

mongoose.connect(process.env.DB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true,
  useCreateIndex: true,
  useFindAndModify: false
})

app.use(cors())
app.use(logger('dev'))
app.use(express.json())
app.use(express.urlencoded({ extended: false }))
app.use(cookieParser())

app.use('/', indexRouter)
app.use(errorHandler)

module.exports = app


0

为什么不分解一个最基本的身份验证模块呢?

SweetAuth

一款轻量级、零配置的用户身份验证模块,不依赖于数据库。

https://www.npmjs.com/package/sweet-auth

就像这样简单:

app.get('/private-page', (req, res) => {

    if (req.user.isAuthorized) {
        // user is logged in! send the requested page
        // you can access req.user.email
    }
    else {
        // user not logged in. redirect to login page
    }
})

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接