如何在node.js中实现登录身份验证


77

我正在运行此节点服务器:

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.session变量(它表示request.session未定义)。

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


Answers:


246

这是我使用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,它有很多auth方法可用)等;关于此的好教程)。


1
Express.js文档说您必须使用连接中间件进行会话管理。您的代码仍然有用吗?
nimrodm 2012年

1
我想你delete res.session.user_id;应该去过delete req.session.user_id;
约翰·库尔

41
这是一个很好的基本解决方案。我想在进行身份验证时添加的一件事是通过编辑响应标头明确告诉浏览器不要缓存“受限制的”页面。在这种情况下,我想补充res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');之前调用next()checkAuth方法。
thauburger 2012年

3
这样可以防止用户访问受限页面,注销并使用后退按钮再次访问受限页面。更改缓存策略将强制页面重新呈现,并使checkAuth方法失败。
thauburger 2012年

4
@BRogers,您可以使用节点bcrypt进行密码哈希处理:github.com/ncb000gt/node.bcrypt.js
alessioalex 2013年

6

实际上,这实际上并不是问题的答案,但这是一种更好的解决方案。

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

除此之外,我建议您使用everyauth进行身份验证。它支持很多身份验证策略。很棒,能快速发展。

通过从文档中粘贴一些副本,可以轻松完成所有这些工作!


3

要添加到Farid的伪答案中,

考虑在everyauth上使用Passport.js

对这些问题的答案这个问题提供一些见解的分歧。


将用户身份验证卸载到Google,Facebook或其他网站有很多好处。如果您的应用程序要求可以将Passport用作唯一的身份验证提供程序或与传统登录一起使用,则可以为用户带来更轻松的体验。


2

@alessioalex答案是适合新节点用户的完美演示。但是无论如何,很难将checkAuth中间件写入除登录名之外的所有路由中,因此最好将app.use的checkAuth从每条路由移至一个条目。例如:

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的课程,但是我更喜欢解析,这就是为什么我不得不寻找最合适的解决方案的原因。这是我自己在expressJS上使用parse的当前解决方案。

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
    }
})
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.