Advertisement
  1. Code
  2. Node.js

Authentifizieren von Node.js-Anwendungen mit Passport

Scroll to top
Read Time: 11 min

German (Deutsch) translation by Tatsiana Bochkareva (you can also view the original English article)

Die Implementierung robuster Authentifizierungsstrategien für jede Anwendung kann eine entmutigende Aufgabe sein, und Node.js-Anwendungen bilden hier keine Ausnahme.

In diesem Tutorial entwickeln wir eine Node.js-Anwendung von Grund auf und verwenden eine relativ neue, aber sehr beliebte Authentifizierungs-Middleware - Passport, um unsere Authentifizierungsprobleme zu lösen.

Die Dokumentation von Passport beschreibt es zu Recht als "einfache, unauffällige Authentifizierungs-Middleware für Node".

Indem Passport sich selbst als Middleware bereitstellt, leistet es hervorragende Arbeit, die anderen Anliegen einer Webanwendung von ihren Authentifizierungsanforderungen zu trennen. Es ermöglicht die einfache Konfiguration von Passport in jede Express-basierte Webanwendung, genau wie wir andere Express-Middleware wie Protokollierung, Body-Parsing, Cookie-Parsing, Session-Handling usw. konfigurieren.

Dieses Tutorial setzt ein grundlegendes Verständnis von Node.js und dem Express-Framework voraus und versucht, sich auf die Authentifizierung zu konzentrieren, obwohl wir eine Express-Beispielanwendung von Grund auf erstellen und fortfahren, indem wir Routen hinzufügen und einige dieser Routen authentifizieren.

Authentifizierungsstrategien

Passport bietet uns über 140 Authentifizierungsmechanismen zur Auswahl. Sie können sich bei einer lokalen/entfernten Datenbankinstanz authentifizieren oder das Single Sign-On mit OAuth-Anbietern für Facebook, Twitter, Google usw. verwenden, um sich bei Ihren Social-Media-Konten zu authentifizieren, oder Sie können aus einer umfangreichen Liste von Anbietern wählen, die die Authentifizierung unterstützen mit Passport und stellen dafür ein Knotenmodul bereit.

Aber keine Sorge: Sie müssen keine Strategie/Mechanismen einbeziehen, die Ihre Anwendung nicht benötigt. Alle diese Strategien sind unabhängig voneinander und als separate Knotenmodule verpackt, die bei der Installation der Middleware von Passport standardmäßig nicht enthalten sind: npm install passport

In diesem Tutorial verwenden wir die lokale Authentifizierungsstrategie von Passport und authentifizieren die Benutzer gegen eine lokal konfigurierte Mongo-DB-Instance, wobei die Benutzerdetails in der Datenbank gespeichert werden. Um die lokale Authentifizierungsstrategie zu verwenden, müssen wir das Modul passport-Local installieren: npm install passport-Local

Aber warten Sie: Bevor Sie Ihr Terminal starten und diese Befehle ausführen, erstellen wir zunächst eine Express-App von Grund auf und fügen ihr einige Routen hinzu (für Login, Registrierung und Home) und versuchen dann, unsere Authentifizierungs-Middleware hinzuzufügen. Beachten Sie, dass wir in diesem Tutorial Express 4 verwenden werden, aber mit einigen kleinen Unterschieden funktioniert Passport auch mit Express 3 genauso gut.

Einrichten der Anwendung

Wenn Sie es noch nicht getan haben, dann installieren Sie Express & express-generator, um eine Boilerplate-Anwendung zu generieren, indem Sie einfach express passport-mongo auf dem Terminal ausführen. Die generierte Anwendungsstruktur sollte wie folgt aussehen:

Initial Application StructureInitial Application StructureInitial Application Structure

Entfernen wir einige der Standardfunktionen, die wir nicht verwenden werden. Löschen Sie die Route users.js und entfernen Sie ihre Referenzen aus der Datei app.js.

Hinzufügen von Projektabhängigkeiten

Öffnen Sie package.json und fügen Sie die Abhängigkeiten für das Modul passport und passport-local hinzu.

1
"passport": "~0.2.0",
2
"passport-local": "~1.0.0"

Da wir die Benutzerdetails in MongoDB speichern, verwenden wir Mongoose als unser Objektdatenmodellierungstool. Eine andere Möglichkeit, die Abhängigkeit von package.json zu installieren und zu speichern, besteht darin, Folgendes einzugeben:

1
npm install mongoose --save 

package.json sollte so aussehen:

Added Mongoose DependenciesAdded Mongoose DependenciesAdded Mongoose Dependencies

Installieren Sie nun alle Abhängigkeiten und führen Sie die Boilerplate-Anwendung aus, indem Sie npm install && npm start ausführen. Es wird nun alle Abhängigkeiten herunterladen und installieren und den Knotenserver starten. Sie können die grundlegende Express-App unter http://localhost:3000/ überprüfen, aber es gibt nicht viel zu sehen.

Wir werden dies sehr bald ändern, indem wir eine vollwertige Express-App erstellen, die nach einer Registrierungsseite für einen neuen Benutzer und dem Login eines registrierten Benutzers fragt und den registrierten Benutzer mit Passport authentifiziert.

Mungo-Modell erstellen

Da wir die Benutzerdetails in Mongo speichern, erstellen wir ein Benutzermodell in Mongoose und speichern dieses in models/user.js in unserer App.

1
var mongoose = require('mongoose');
2
3
module.exports = mongoose.model('User',{
4
        username: String,
5
	password: String,
6
	email: String,
7
	gender: String,
8
	address: String
9
});

Grundsätzlich erstellen wir ein Mongoose-Modell, mit dem wir CRUD-Operationen an der zugrunde liegenden Datenbank durchführen können.

Mongo konfigurieren

Wenn Sie Mongo nicht lokal installiert haben, empfehlen wir Ihnen, Cloud-Datenbankdienste wie Modulus oder MongoLab zu verwenden. Das Erstellen einer funktionierenden MongoDB-Instanz mit diesen ist nicht nur kostenlos, sondern nur mit wenigen Klicks möglich.

Nachdem Sie eine Datenbank auf einem dieser Dienste erstellt haben, erhalten Sie eine Datenbank-URI wie mongodb://<dbuser>:<dbpassword>@novus.modulusmongo.net:27017/<dbName>, die verwendet werden kann, um CRUD-Operationen auf . auszuführen die Datenbank. Es ist eine gute Idee, die Datenbankkonfiguration in einer separaten Datei zu speichern, die bei Bedarf abgerufen werden kann. Als solches erstellen wir ein Knotenmodul db.js, das wie folgt aussieht:

1
module.exports = {
2
  'url' : 'mongodb://<dbuser>:<dbpassword>@novus.modulusmongo.net:27017/<dbName>'
3
}

Wenn Sie wie ich eine lokale Mongo-Instanz verwenden, ist es an der Zeit, den mongod-Daemon zu starten und die db.js sollte so aussehen

1
module.exports = {
2
  'url' : 'mongodb://localhost/passport'
3
}

Jetzt verwenden wir diese Konfiguration in app.js und stellen eine Verbindung mit Mongoose-APIs her:

1
var dbConfig = require('./db.js');
2
var mongoose = require('mongoose');
3
mongoose.connect(dbConfig.url);

Passport konfigurieren

Passport stellt nur den Mechanismus zur Verfügung, um die Authentifizierung zu handhaben, wobei die Verantwortung für die Implementierung der Sitzungsbehandlung selbst besteht, und dafür werden wir express-session verwenden. Öffnen Sie app.js und fügen Sie den folgenden Code ein, bevor Sie die Routen konfigurieren:

1
// Configuring Passport

2
var passport = require('passport');
3
var expressSession = require('express-session');
4
app.use(expressSession({secret: 'mySecretKey'}));
5
app.use(passport.initialize());
6
app.use(passport.session());

Dies ist erforderlich, da wir möchten, dass unsere Benutzersitzungen von Natur aus persistent sind. Bevor wir die App ausführen, müssen wir express-session installieren und zu unserer Abhängigkeitsliste in package.json hinzufügen. Geben Sie dazu npm install --save express-session ein.

Serialisieren und Deserialisieren von Benutzerinstanzen

Passport muss auch die Benutzerinstanz aus einem Sitzungsspeicher serialisieren und deserialisieren, um Anmeldesitzungen zu unterstützen, sodass jede nachfolgende Anforderung nicht die Benutzeranmeldeinformationen enthält. Dafür stehen zwei Methoden serializeUser und deserializeUser zur Verfügung:

1
passport.serializeUser(function(user, done) {
2
  done(null, user._id);
3
});
4
5
passport.deserializeUser(function(id, done) {
6
  User.findById(id, function(err, user) {
7
    done(err, user);
8
  });
9
});

Verwenden von Passstrategien

Wir werden nun die Strategien von Passport für den Umgang mit Login und Registrierung definieren. Jeder von ihnen wäre eine Instanz der lokalen Authentifizierungsstrategie von Passport und würde mit passport.use() erstellt Funktion. Wir verwenden connect-flash, um uns bei der Fehlerbehandlung zu helfen, indem wir Flash-Nachrichten bereitstellen, die dem Benutzer bei Fehlern angezeigt werden können.

Anmeldestrategie

Die Login-Strategie sieht wie folgt aus:

1
// passport/login.js

2
passport.use('login', new LocalStrategy({
3
    passReqToCallback : true
4
  },
5
  function(req, username, password, done) { 
6
    // check in mongo if a user with username exists or not

7
    User.findOne({ 'username' :  username }, 
8
      function(err, user) {
9
        // In case of any error, return using the done method

10
        if (err)
11
          return done(err);
12
        // Username does not exist, log error & redirect back

13
        if (!user){
14
          console.log('User Not Found with username '+username);
15
          return done(null, false, 
16
                req.flash('message', 'User Not found.'));                 
17
        }
18
        // User exists but wrong password, log the error 

19
        if (!isValidPassword(user, password)){
20
          console.log('Invalid Password');
21
          return done(null, false, 
22
              req.flash('message', 'Invalid Password'));
23
        }
24
        // User and password both match, return user from 

25
        // done method which will be treated like success

26
        return done(null, user);
27
      }
28
    );
29
}));

Der erste Parameter von passport.use() ist der Name der Strategie, die verwendet wird, um diese Strategie zu identifizieren, wenn sie später angewendet wird. Der zweite Parameter ist die Art der Strategie, die Sie erstellen möchten, hier verwenden wir das username-password oder die LocalStrategy. Es ist zu beachten, dass LocalStrategy standardmäßig erwartet, die Benutzeranmeldeinformationen in den Parametern username und password zu finden, aber es erlaubt uns auch, alle anderen benannten Parameter zu verwenden. Die Konfigurationsvariable passReqToCallback ermöglicht es uns, auf das request-Objekt im Callback zuzugreifen, wodurch wir jeden Parameter verwenden können, der mit der Anfrage verbunden ist.

Als Nächstes verwenden wir die Mongoose-API, um den Benutzer in unserer zugrunde liegenden Sammlung von Benutzern zu finden, um zu überprüfen, ob der Benutzer ein gültiger Benutzer ist oder nicht. Der letzte Parameter in unserem Callback : done bezeichnet eine nützliche Methode, mit der wir dem Passport-Modul Erfolg oder Misserfolg signalisieren können. Um einen Fehler anzugeben, sollte entweder der erste Parameter den Fehler enthalten oder der zweite Parameter sollte als false ausgewertet werden. Um den Erfolg zu signalisieren, sollte der erste Parameter null sein und der zweite Parameter sollte einen truthy wert ergeben. In diesem Fall wird er für das request-Objekt verfügbar gemacht

Da Kennwörter von Natur aus schwach sind, sollten wir sie immer verschlüsseln, bevor wir sie in der Datenbank speichern. Dazu verwenden wir bcrypt-nodejs, um uns bei der Verschlüsselung und Entschlüsselung von Passwörtern zu helfen.

1
var isValidPassword = function(user, password){
2
  return bCrypt.compareSync(password, user.password);
3
}

Wenn Sie sich bei den Code-Snippets nicht wohl fühlen und lieber den vollständigen Code in Aktion sehen möchten, können Sie den Code hier durchsuchen.

Registrierungsstrategie

Jetzt definieren wir die nächste Strategie, die die Registrierung eines neuen Benutzers handhabt und seinen oder ihren Eintrag in unserer zugrunde liegenden Mongo-DB erstellt:

1
passport.use('signup', new LocalStrategy({
2
    passReqToCallback : true 
3
  },
4
  function(req, username, password, done) {
5
    findOrCreateUser = function(){
6
      // find a user in Mongo with provided username

7
      User.findOne({'username':username},function(err, user) {
8
        // In case of any error return

9
        if (err){
10
          console.log('Error in SignUp: '+err);
11
          return done(err);
12
        }
13
        // already exists

14
        if (user) {
15
          console.log('User already exists');
16
          return done(null, false, 
17
             req.flash('message','User Already Exists'));
18
        } else {
19
          // if there is no user with that email

20
          // create the user

21
          var newUser = new User();
22
          // set the user's local credentials

23
          newUser.username = username;
24
          newUser.password = createHash(password);
25
          newUser.email = req.param('email');
26
          newUser.firstName = req.param('firstName');
27
          newUser.lastName = req.param('lastName');
28
29
          // save the user

30
          newUser.save(function(err) {
31
            if (err){
32
              console.log('Error in Saving user: '+err);  
33
              throw err;  
34
            }
35
            console.log('User Registration succesful');    
36
            return done(null, newUser);
37
          });
38
        }
39
      });
40
    };
41
    
42
    // Delay the execution of findOrCreateUser and execute 

43
    // the method in the next tick of the event loop

44
    process.nextTick(findOrCreateUser);
45
  });
46
);

Auch hier verwenden wir die Mongoose-API, um herauszufinden, ob ein Benutzer mit dem angegebenen Benutzernamen bereits existiert oder nicht. Wenn nicht, erstellen Sie einen neuen Benutzer und speichern Sie die Benutzerinformationen in Mongo. Andernfalls geben Sie den Fehler mit den done-Callback- und Flash-Nachrichten zurück. Beachten Sie, dass wir bcrypt-nodejs verwenden, um den Hash des Passworts zu erstellen, bevor wir es speichern:

1
// Generates hash using bCrypt

2
var createHash = function(password){
3
 return bCrypt.hashSync(password, bCrypt.genSaltSync(10), null);
4
}

Routen erstellen

Wenn wir unsere Anwendung aus der Vogelperspektive betrachten würden, würde sie so aussehen:

Birds Eye View of Our ApplicationBirds Eye View of Our ApplicationBirds Eye View of Our Application

Wir definieren nun unsere Routen für die Anwendung im folgenden Modul, das die oben in app.js erstellte Instanz von Passport verwendet. Speichern Sie dieses Modul in routes/index.js

1
module.exports = function(passport){
2
3
  /* GET login page. */
4
  router.get('/', function(req, res) {
5
    // Display the Login page with any flash message, if any

6
    res.render('index', { message: req.flash('message') });
7
  });
8
9
  /* Handle Login POST */
10
  router.post('/login', passport.authenticate('login', {
11
	successRedirect: '/home',
12
	failureRedirect: '/',
13
	failureFlash : true  
14
  }));
15
16
  /* GET Registration Page */
17
  router.get('/signup', function(req, res){
18
	res.render('register',{message: req.flash('message')});
19
  });
20
21
  /* Handle Registration POST */
22
  router.post('/signup', passport.authenticate('signup', {
23
	successRedirect: '/home',
24
	failureRedirect: '/signup',
25
	failureFlash : true  
26
  }));
27
28
  return router;
29
}

Der wichtigste Teil des obigen Code-Schnipsels ist die Verwendung von passport.authenticate() um die Authentifizierung an login und signup zu delegieren, wenn ein HTTP-POST an die /login- bzw. /signup-Routen erfolgt. Beachten Sie, dass es nicht zwingend erforderlich ist, die Strategien auf dem Routenpfad zu benennen und er kann beliebig benannt werden.

Erstellen von Jadeansichten

Als nächstes erstellen wir die folgenden zwei Ansichten für unsere Anwendung:

  1. layout.jade enthält die grundlegenden Layout- und Styling-Informationen
  2. index.jade enthält die Login-Seite mit dem Login-Formular und der Möglichkeit, ein neues Konto zu erstellen
1
extends layout
2
3
block content
4
  div.container
5
	div.row
6
	  div.col-sm-6.col-md-4.col-md-offset-4
7
		h1.text-center.login-title Sign in to our Passport app
8
		  div.account-wall
9
			img(class='profile-img', src='https://lh5.googleusercontent.com/-b0-k99FZlyE/AAAAAAAAAAI/AAAAAAAAAAA/eu7opA4byxI/photo.jpg?sz=120')
10
			form(class='form-signin', action='/login', method='POST')
11
			  input(type='text', name='username' class='form-control', placeholder='Email',required, autofocus)
12
			  input(type='password', name='password' class='form-control', placeholder='Password', required)
13
			  button(class='btn btn-lg btn-primary btn-block', type='submit') Sign in
14
			  span.clearfix
15
		  a(href='/signup', class='text-center new-account') Create an account
16
		  #message
17
		  if message
18
			h1.text-center.error-message #{message}

Dank Bootstrap sieht unsere Login-Seite jetzt so aus

Login Page for Our Passport AppLogin Page for Our Passport AppLogin Page for Our Passport App

Wir benötigen zwei weitere Ansichten für die Registrierungsdetails und für die Startseite der Anwendung:

  1. register.jade enthält das Anmeldeformular
  2. home.jade sagt hallo und zeigt eingeloggte Benutzerdaten an

Wenn Sie mit Jade nicht vertraut sind, lesen Sie die Dokumentation.

Implementieren der Abmeldefunktion

Passport, eine Middleware, darf bestimmte Eigenschaften und Methoden zu Anforderungs- und Antwortobjekten hinzufügen und verwendet sie richtig, indem es eine sehr praktische hinzufügt request.logout() -Methode, die die Benutzersitzung neben anderen Eigenschaften ungültig macht.

1
/* Handle Logout */
2
router.get('/signout', function(req, res) {
3
  req.logout();
4
  res.redirect('/');
5
});

Routen schützen

Passport bietet auch die Möglichkeit, den Zugang zu einer Route zu schützen, die für einen anonymen Benutzer als ungeeignet erachtet wird. Das bedeutet, dass, wenn ein Benutzer versucht, auf http://localhost:3000/home zuzugreifen, ohne sich in der Anwendung zu authentifizieren, er auf die Startseite umgeleitet wird, indem er Folgendes tut

1
/* GET Home Page */
2
router.get('/home', isAuthenticated, function(req, res){
3
  res.render('home', { user: req.user });
4
});
5
6
// As with any middleware it is quintessential to call next()

7
// if the user is authenticated

8
var isAuthenticated = function (req, res, next) {
9
  if (req.isAuthenticated())
10
	return next();
11
  res.redirect('/');
12
}

Abschluss

Passport ist nicht der einzige Spieler in diesem Bereich, wenn es um die Authentifizierung von Node.js-Anwendungen geht, und es gibt Alternativen wie EveryAuth, aber die Modularität, Flexibilität, Community-Unterstützung und die Tatsache, dass es sich nur um eine Middleware handelt, machen Passport definitiv zu einer viel besseren Wahl.

Für einen detaillierten Vergleich zwischen den beiden hier eine interessante und informative Perspektive des Entwicklers von Passport selbst.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.