So implementieren Sie die Anmeldeauthentifizierung in node.js.


77

Ich habe diesen Knotenserver ausgeführt:

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);
    }
});

Ich möchte ein Anmeldeformular für diesen Server hinzufügen. Wenn der Benutzer authentifiziert ist, wird es angezeigt.

   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;
    }    
}

In der Prozessabfragefunktion werden die Dateien an den Client zurückgegeben, wenn kein Befehl angegeben wird, sodass ich den Anmeldebefehl vom Client an den Server senden kann. Was soll der Server tun, wenn er den Anmeldebefehl mit dem Benutzernamen-Kennwort erhält? Wie soll er die Anmeldeanforderung übergeben? und senden Sie den Anmeldeerfolg oder -fehler zurück, um diesen Teil zu schreiben, brauche ich Hilfe.

was ich versucht habe.

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);
    }
}

Bitte schlagen Sie vor, wie ich eine Sitzung hinzufügen kann. Ich habe versucht, eine Anforderungsvariable in der Anmeldefunktion hinzuzufügen und die request.session-Variable festzulegen, die besagt, dass request.session undefiniert ist.

Bitte schlagen Sie vor, wie ich dieses Anmeldemodul schreiben kann, das die Anmeldeauthentifizierung für jeden Benutzer ordnungsgemäß verwalten kann.


Antworten:


246

So mache ich das mit Express.js :

1) Überprüfen Sie, ob der Benutzer authentifiziert ist : Ich habe eine Middleware-Funktion namens CheckAuth, die ich auf jeder Route verwende, auf der der Benutzer authentifiziert werden muss:

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

Ich benutze diese Funktion in meinen Routen wie folgt:

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

2) Die Login-Route:

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) Die Abmelderoute:

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

Wenn Sie mehr über Express.js erfahren möchten, besuchen Sie die Website hier: expressjs.com/en/guide/routing.html Wenn Sie komplexere Dinge benötigen, schauen Sie sich everyauth an (es stehen viele Authentifizierungsmethoden für Facebook und Twitter zur Verfügung usw .; gutes Tutorial dazu hier ).


1
In der Express.js-Dokumentation heißt es, dass Sie die Connect-Middleware für die Sitzungsverwaltung verwenden müssen. Ist Ihr Code noch relevant?
Nimrodm

1
Ich denke du delete res.session.user_id;hättest es sein sollen delete req.session.user_id;.
Johan Kool

41
Dies ist eine großartige Basislösung. Eine Sache, die ich bei der Authentifizierung hinzufügen möchte, ist die explizite Anweisung an den Browser, "eingeschränkte" Seiten nicht zwischenzuspeichern, indem der Antwortheader bearbeitet wird. In diesem Fall würde ich hinzufügen, res.header('Cache-Control', 'no-cache, private, no-store, must-revalidate, max-stale=0, post-check=0, pre-check=0');bevor ich next()die checkAuthMethode aufrufe.
Thauburger

3
Dies verhindert, dass ein Benutzer auf eine eingeschränkte Seite zugreift, sich abmeldet und dann über die Schaltfläche "Zurück" erneut auf die eingeschränkte Seite zugreift. Durch Ändern der Cache-Richtlinie wird die Seite zum erneuten Rendern gezwungen, und die checkAuthMethode schlägt fehl .
Thauburger

4
@ BRogers können Sie Knoten bcrypt für Passwort-Hashing verwenden: github.com/ncb000gt/node.bcrypt.js
alessioalex

6

Eigentlich ist dies nicht wirklich die Antwort auf die Frage, aber dies ist ein besserer Weg, dies zu tun.

Ich empfehle Ihnen, connect / express als http-Server zu verwenden, da Sie dadurch viel Zeit sparen. Sie wollen das Rad offensichtlich nicht neu erfinden. In Ihrem Fall ist die Sitzungsverwaltung mit Connect / Express viel einfacher.

Außerdem empfehle ich Ihnen zur Authentifizierung die Verwendung von everyauth . Welches unterstützt viele Authentifizierungsstrategien. Genial für die schnelle Entwicklung.

All dies kann leicht durch Einfügen von Kopien aus der Dokumentation behoben werden!


3

Um Farids Pseudo-Antwort hinzuzufügen,

Erwägen Sie die Verwendung von Passport.js über jeden Tag .

Die Antworten auf diese Frage geben einen Einblick in die Unterschiede.


Das Auslagern Ihrer Benutzerauthentifizierung auf Google, Facebook oder eine andere Website bietet zahlreiche Vorteile. Wenn die Anforderungen Ihrer Anwendung so sind, dass Sie Passport als einzigen Authentifizierungsanbieter oder neben der herkömmlichen Anmeldung verwenden können, kann dies Ihren Benutzern die Benutzererfahrung erleichtern.


2

@alessioalex answer ist eine perfekte Demo für neue Knotenbenutzer. Trotzdem ist es schwierig, checkAuth-Middleware in alle Routen außer Login zu schreiben. Daher ist es besser, checkAuth mit app.use von jeder Route zu einem Eintrag zu verschieben. Zum Beispiel:

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

Ich habe diese Antwort ausprobiert und es hat bei mir nicht funktioniert. Ich bin auch ein Neuling in der Webentwicklung und habe Kurse besucht, in denen ich mlab verwendet habe, aber ich bevorzuge das Parsen, weshalb ich nach der am besten geeigneten Lösung suchen musste. Hier ist meine aktuelle Lösung mit parse on expressJS.

1) Überprüfen Sie, ob der Benutzer authentifiziert ist: Ich habe eine Middleware-Funktion namens isLogginIn, die ich auf jeder Route verwende, auf der der Benutzer authentifiziert werden muss:

 function isLoggedIn(req, res, next) {
 var currentUser = Parse.User.current();
 if (currentUser) {
     next()
 } else {
     res.send("you are not authorised");
 }
}

Ich benutze diese Funktion in meinen Routen wie folgt:

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

2) Die Login-Route:

  // 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) Die Abmelderoute:

 // 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');
   });

Dies hat bei mir sehr gut funktioniert und ich habe auf die Dokumentation hier https://docs.parseplatform.org/js/guide/#users vollständig verwiesen

Vielen Dank an @alessioalex für seine Antwort. Ich habe nur mit den neuesten Praktiken aktualisiert.


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

Warum nicht ein Minimum-Authentifizierungsmodul entfernen?

SweetAuth

Ein leichtes Benutzerauthentifizierungsmodul ohne Konfiguration, das nicht von einer Datenbank abhängig ist.

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

Es ist so einfach wie:

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
    }
})
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.