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

Posted

技术标签:

【中文标题】如何在 node.js 中实现登录身份验证【英文标题】:how to implement login auth in node.js 【发布时间】:2011-12-20 21:52:27 【问题描述】:

我正在运行这个节点服务器:

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 未定义。

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

【问题讨论】:

完成连接:blog.nodejitsu.com/sessions-and-cookies-in-node 【参考方案1】:

这是我使用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)。

【讨论】:

Express.js 文档说您必须使用连接中间件进行会话管理。您的代码仍然相关吗? 我认为你的 delete res.session.user_id; 应该是 delete req.session.user_id; 这是一个很棒的基本解决方案。在进行身份验证时我喜欢添加的一件事是明确告诉浏览器不要通过编辑响应标头来缓存“受限”页面。在这种情况下,我会在checkAuth 方法中调用next() 之前添加res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0'); 这可以防止用户访问受限页面、注销,然后使用返回按钮再次访问受限页面。更改缓存策略会强制页面重新呈现,并使checkAuth 方法失败。 @BRogers 您可以使用节点 bcrypt 进行密码散列:github.com/ncb000gt/node.bcrypt.js【参考方案2】:

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

我建议你使用connect/express 作为 http 服务器,因为它们可以为你节省很多时间。你显然不想重新发明***。在您的情况下,使用 connect/express 进行会话管理要容易得多。

除此之外,我建议您使用everyauth 进行身份验证。它支持很多身份验证策略。非常适合快速开发。

所有这些都可以通过从他们的文档中复制粘贴来轻松解决!

【讨论】:

【参考方案3】:

要添加到 Farid 的伪答案,

考虑使用Passport.js 而不是everyauth。

this question 的答案提供了一些关于差异的见解。


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

【讨论】:

【参考方案4】:

@alessioalex 答案是新节点用户的完美演示。但无论如何,除了登录之外,很难将 checkAuth 中间件写入所有路由,因此最好将 checkAuth 从每个路由移动到 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)

【讨论】:

【参考方案5】:

我尝试了这个答案,但它对我不起作用。我也是 Web 开发的新手,在我使用 mlab 的地方上过课,但我更喜欢 parse,这就是为什么我必须寻找最合适的解决方案。这是我自己当前在 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 的回答。我只更新了最新的做法。

【讨论】:

【参考方案6】:

======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

【讨论】:

【参考方案7】:

为什么不剖析一个最低限度的身份验证模块?

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
    
)

【讨论】:

以上是关于如何在 node.js 中实现登录身份验证的主要内容,如果未能解决你的问题,请参考以下文章

如何在基于 JWT 的单点登录身份验证架构中实现注销?

如何在 Firebase 身份验证中实现多用户帐户登录和切换?

如何在 WCF 服务中实现自定义身份验证

如何在 Next.js 中实现身份验证

使用 Account Manager 在 Android 中实现 JWT 身份验证

如何在视图模型中实现 Firebase 电话身份验证?