Einen großartigen Nest.js-Blog erstellen: Autorisierung hinzufügen
Takashi Yamamoto
Infrastructure Engineer · Leapcell

Im vorherigen Tutorial haben wir erfolgreich ein Benutzeregistrierungssystem und grundlegende Anmeldevalidierungslogik erstellt. Benutzer können Konten erstellen, und die Anwendung kann ihre Benutzernamen und Passwörter überprüfen.
Die aktuelle Anmeldung ist jedoch ein einmaliges Ereignis. Der Server merkt sich den Anmeldestatus des Benutzers nicht. Jedes Mal, wenn die Seite aktualisiert wird, wird der Benutzer wieder zum Gast.
In diesem Artikel werden wir express-session
verwenden, um eine echte Verwaltung des Benutzeranmeldestatus für unseren Blog zu implementieren, Seiten und Funktionen zu schützen, die eine Anmeldung erfordern, und die Benutzeroberfläche dynamisch basierend auf dem Anmeldestatus des Benutzers zu aktualisieren.
Sitzung konfigurieren
Zur Verwaltung von Sitzungen verwenden wir express-session
, eine sehr beliebte Bibliothek im Express-Ökosystem. Da Nest.js standardmäßig Express im Hintergrund verwendet, können wir es direkt integrieren.
Installieren Sie die notwendigen Abhängigkeiten:
npm install express-session npm install -D @types/express-session
Redis zur Speicherung von Sitzungen verwenden
Standardmäßig speichert express-session
Sitzungen im Arbeitsspeicher des Servers. Das bedeutet, dass alle Anmeldestatus der Benutzer verloren gehen, wenn der Server neu gestartet wird. Um dies zu lösen, werden wir Redis, eine Hochleistungs-In-Memory-Datenbank, verwenden, um Sitzungen persistent zu machen.
Was, wenn Sie kein Redis haben?
Sie können eine Redis-Instanz auf Leapcell erstellen. Leapcell bietet die meisten Werkzeuge, die eine Backend-Anwendung benötigt!
Klicken Sie in der Benutzeroberfläche auf die Schaltfläche "Redis erstellen", um eine neue Redis-Instanz zu erstellen.
Die Detailseite von Redis bietet eine Online-CLI, mit der Sie Redis-Befehle direkt ausführen können.
Wenn Ihnen absolut kein Redis-Dienst zur Verfügung steht, wird express-session
automatisch auf die In-Memory-Speicherung zurückgreifen. Dies ist jedoch keine Best Practice für Produktionsumgebungen und kann zu potenziellen Problemen führen.
Installieren Sie die Redis-bezogenen Abhängigkeiten:
npm install redis connect-redis
Öffnen Sie nun die Datei src/main.ts
, um die Sitzungs-Middleware zu konfigurieren und eine Verbindung zu Redis herzustellen.
// src/main.ts import { NestFactory } from '@nestjs/core'; import { AppModule } from './app.module'; import { NestExpressApplication } from '@nestjs/platform-express'; import { join } from 'path'; import session from 'express-session'; import { createClient } from 'redis'; import { RedisStore } from 'connect-redis'; async function bootstrap() { const app = await NestFactory.create<NestExpressApplication>(AppModule); // Initialisiere den Redis-Client const redisClient = createClient({ // Wenn Ihr Redis ein Passwort hat oder auf einem anderen Host liegt, ändern Sie die Konfiguration hier // url: 'redis://:password@hostname:port' url: 'redis://localhost:6379', }); await redisClient.connect().catch(console.error); // Initialisiere RedisStore const redisStore = new RedisStore({ client: redisClient, prefix: 'blog-session:', }); app.use( session({ store: redisStore, // Redis für die Speicherung verwenden secret: 'your-secret-key', // Ersetzen Sie dies durch eine zufällige, komplexe Zeichenkette resave: false, saveUninitialized: false, cookie: { httpOnly: true, maxAge: 1000 * 60 * 60 * 24 * 7, // 7 Tage }, }) ); app.useStaticAssets(join(__dirname, '..', 'public')); app.setBaseViewsDir(join(__dirname, '..', 'views')); app.setViewEngine('ejs'); await app.listen(3000); } bootstrap();
Sobald die Middleware express-session
konfiguriert ist, wird sie jede Anfrage automatisch bearbeiten, die Cookie parsen und die entsprechenden Sitzungsdaten dem request.session
-Objekt beifügen.
Echte Anmelde- und Abmelderouten implementieren
Nachdem die Projektkonfiguration abgeschlossen ist, aktualisieren wir auth.controller.ts
, um die Anmelde- und Abmelde-Logik zu verarbeiten.
// src/auth/auth.controller.ts import { Controller, Get, Post, Render, Body, Request, Res, UnauthorizedException } from '@nestjs/common'; import { AuthService } from './auth.service'; import { Response } from 'express'; @Controller('auth') export class AuthController { constructor(private authService: AuthService) {} @Get('login') @Render('login') showLoginForm() { return; } @Post('login') async login(@Request() req, @Body() body, @Res() res: Response) { const user = await this.authService.validateUser(body.username, body.password); if (!user) { throw new UnauthorizedException('Ungültige Anmeldedaten'); } // Bei erfolgreicher Validierung Benutzerinformationen manuell in der Sitzung speichern req.session.user = { id: user.id, username: user.username }; res.redirect('/posts'); } @Get('logout') logout(@Request() req, @Res() res: Response) { // session.destroy() aufrufen, um die Sitzung zu löschen req.session.destroy((err) => { if (err) { console.log(err); } res.redirect('/'); // Nach dem Abmelden zur Startseite umleiten }); } }
In der login
-Methode validieren wir die Anmeldeinformationen des Benutzers.
Nach erfolgreicher Validierung speichern wir ein Objekt mit grundlegenden Benutzerinformationen in req.session.user
. express-session
speichert diese Sitzung automatisch in Redis und setzt ein Cookie im Browser, das die Session-ID enthält.
Basierend auf dem Cookie-Mechanismus trägt der Browser dieses Cookie bei nachfolgenden Anfragen automatisch mit. Der Server extrahiert die Session-ID daraus und erkennt so den Anmeldestatus des Benutzers.
In der logout
-Methode rufen wir req.session.destroy()
auf, was die Sitzung aus Redis löscht und den Benutzer somit abmeldet.
Routen schützen und Benutzeroberfläche aktualisieren
Nachdem wir nun einen Anmeldemechanismus haben, ist der letzte Schritt, diesen zum Schutz unseres "Neuer Beitrag"-Features zu verwenden und eine andere Benutzeroberfläche basierend auf dem Anmeldestatus anzuzeigen.
Eine Authentifizierungs-Guard erstellen
A Guard ist eine Klasse in Nest.js, die bestimmt, ob eine Anfrage behandelt werden soll. Wir erstellen einen AuthenticatedGuard
, um zu überprüfen, ob ein Benutzer angemeldet ist.
Erstellen Sie authenticated.guard.ts
im Verzeichnis src/auth
:
// src/auth/authenticated.guard.ts import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common'; @Injectable() export class AuthenticatedGuard implements CanActivate { async canActivate(context: ExecutionContext): Promise<boolean> { const request = context.switchToHttp().getRequest(); // Prüfen, ob ein Benutzerobjekt in der Sitzung vorhanden ist return Boolean(request.session.user); } }
Die Logik dieses Guards ist sehr einfach: Wenn request.session.user
vorhanden ist, bedeutet dies, dass der Benutzer angemeldet ist, und er gibt true
zurück, wodurch die Anfrage fortgesetzt werden kann. Andernfalls gibt er false
zurück, und Nest.js lehnt die Anfrage automatisch ab.
Den Guard anwenden
Öffnen Sie src/posts/posts.controller.ts
und wenden Sie den Dekorator @UseGuards(AuthenticatedGuard)
auf die Routen an, die geschützt werden müssen.
// src/posts/posts.controller.ts import { Controller, Get, Render, Post, Body, Res, UseGuards, Param } from '@nestjs/common'; import { AuthenticatedGuard } from '../auth/authenticated.guard'; // Der Pfad muss möglicherweise angepasst werden import { PostsService } from './posts.service'; import type { Response } from 'express'; @Controller('posts') export class PostsController { constructor(private readonly postsService: PostsService) {} // ... findAll() und findOne() @UseGuards(AuthenticatedGuard) // <--- Guard anwenden @Get('new') @Render('new-post') newPostForm() { return; } @UseGuards(AuthenticatedGuard) // <--- Guard anwenden @Post() async create(@Body() body: { title: string; content: string }, @Res() res: Response) { await this.postsService.create(body); res.redirect('/posts'); } // ... }
Wenn nun ein nicht angemeldeter Benutzer versucht, auf /posts/new
zuzugreifen, wird er automatisch abgefangen (standardmäßig erhält er einen Fehler 403 Forbidden
).
Die Frontend-Benutzeroberfläche aktualisieren
Schließlich aktualisieren wir die Benutzeroberfläche, um verschiedene Schaltflächen basierend auf dem Anmeldestatus des Benutzers anzuzeigen. Wir müssen den Anmeldestatus des Benutzers (req.session.user
) an das Frontend übergeben, wenn wir die Webvorlagen rendern.
Ändern Sie views/_header.ejs
, um Links für Anmeldung/Registrierung und Abmeldung/Neuer Beitrag hinzuzufügen.
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <title><%= title %></title> <link rel="stylesheet" href="/css/style.css" /> </head> <body> <header> <h1><a href="/">My Blog</a></h1> <div class="user-actions"> <% if (user) { %> <span>Willkommen, <%= user.username %></span> <a href="/posts/new" class="new-post-btn">Neuer Beitrag</a> <a href="/auth/logout">Logout</a> <% } else { %> <a href="/auth/login">Anmelden</a> <a href="/users/register">Registrieren</a> <% } %> </div> </header> <main>
Damit der obige Code funktioniert, müssen wir unsere Controller aktualisieren, um die user
-Informationen an die Views zu übergeben.
Ändern Sie in posts.controller.ts
alle Methoden, die Views rendern:
// src/posts/posts.controller.ts import { Controller, Get, Render, Param, /*...,*/ Request } from '@nestjs/common'; // ... @Controller('posts') export class PostsController { // ... @Get() @Render('index') async root(@Request() req) { const posts = await this.postsService.findAll(); return { posts, user: req.session.user }; // Benutzer aus der Sitzung an die Vorlage übergeben } @UseGuards(AuthenticatedGuard) @Get('new') @Render('new-post') newPostForm(@Request() req) { return { user: req.session.user }; // Benutzer aus der Sitzung an die Vorlage übergeben } // ... @Get(':id') @Render('post') async findOne(@Param('id') id: string, @Request() req) { const post = await this.postsService.findOne(id); return { post, user: req.session.user }; // Benutzer aus der Sitzung an die Vorlage übergeben } }
Andere Controller, die views/_header.ejs
verwenden, wie z. B. auth.controller.ts
und users.controller.ts
, müssen die gleiche Anpassung vornehmen.
Ausführen und Testen
Starten Sie nun Ihre Anwendung neu:
npm run start:dev
Besuchen Sie http://localhost:3000
.
Sie sehen oben rechts die Schaltflächen "Anmelden" und "Registrieren". Registrieren Sie ein Konto und melden Sie sich an.
Nach erfolgreicher Anmeldung werden Sie zur Homepage weitergeleitet, und oben rechts sehen Sie die Schaltflächen "Willkommen, [Ihr Benutzername]", "Neuer Beitrag" und "Logout".
Sie können nun auf "Neuer Beitrag" klicken, um einen neuen Artikel zu erstellen. Wenn Sie sich abmelden und dann versuchen, /posts/new
aufzurufen, werden Sie blockiert.
Damit haben wir ein vollständiges Benutzerauthentifizierungssystem zu unserem Blog hinzugefügt. Machen Sie sich keine Sorgen mehr, dass Freunde Ihre Beiträge manipulieren!
Weitere Übungen
Wir haben ein Authentifizierungssystem manuell mit express-session
implementiert.
In realen Szenarien haben Sie jedoch oft komplexere Authentifizierungsanforderungen, wie z. B. die Anmeldung über Drittkonten wie Google, GitHub usw. In solchen Fällen sollten Sie eine ausgereiftere Authentifizierungsbibliothek wie Passport.js für die Anmeldung und Autorisierung in Betracht ziehen.
Wenn Sie daran interessiert sind, versuchen Sie, die Anmeldeautorisierung des Projekts mit Passport.js umzugestalten!
Frühere Tutorials: