() translation by (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:



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:



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:



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:
-
layout.jade
enthält die grundlegenden Layout- und Styling-Informationen -
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



Wir benötigen zwei weitere Ansichten für die Registrierungsdetails und für die Startseite der Anwendung:
-
register.jade
enthält das Anmeldeformular -
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.