sql >> Database teknologi >  >> NoSQL >> MongoDB

NestJS:Sådan implementeres sessionsbaseret brugergodkendelse

Introduktion

Det er en indiskutabel realitet, at autentificering er afgørende i enhver applikation eller ethvert system, hvis du vil sikre brugerdata og muliggøre sikker adgang til information. Autentificering er proceduren til at fastslå eller demonstrere, at noget er sandt, legitimt eller gyldigt.

Forudsætninger

Denne tutorial er en praktisk demonstration. For at følge med skal du sørge for at have følgende på plads:

  • Node.js kører i dit system, fordi NestJS er en Node.js-ramme
  • MongoDB installeret

Hvad er NestJS?

Nest (NestJS) er en Node.js server-side applikationsramme til opbygning af skalerbare, effektive applikationer.

Det er skrevet i TypeScript og bygget på Express, en meget minimalistisk ramme, der er fantastisk i sig selv, men mangler struktur. Den kombinerer programmeringsparadigmer såsom objektorienteret programmering, funktionel programmering og funktionel reaktiv programmering.

Det er en ramme at bruge, hvis du vil have meget struktur på din backend. Dens syntaks og struktur ligner meget AngularJS, en front-end-ramme. Og den bruger TypeScript, tjenester og afhængighedsinjektion på samme måde, som AngularJS gør.

Den anvender moduler og controllere, og du kan bygge controllere til en fil ved hjælp af kommandolinjegrænsefladen.

NestJS-moduler giver dig mulighed for at gruppere relaterede controllere og tjenesteudbydere i en enkelt kodefil. Kort sagt er et NestJS-modul en TypeScript-fil med @Module anmærkning (). Denne dekoratør informerer NestJS-rammen om, hvilke controllere, tjenesteudbydere og andre tilknyttede ressourcer, der senere vil blive instansieret og brugt af appkoden.

Hvad er sessionsbaseret godkendelse?

Sessionsbaseret godkendelse er en metode til brugergodkendelse, hvor serveren opretter en session efter et vellykket login, med sessions-id'et gemt i en cookie eller lokalt lager i din browser.

Ved efterfølgende anmodninger valideres din cookie mod det sessions-id, der er gemt på serveren. Hvis der er et match, anses anmodningen for gyldig og behandlet.

Når du bruger denne godkendelsesmetode, er det vigtigt at have følgende bedste praksis for sikkerhed i tankerne:

  • Generer lange og tilfældige sessions-id'er (128 bit er den anbefalede længde) for at gøre brute force-angreb ineffektive
  • Undgå at gemme følsomme eller brugerspecifikke data
  • Gør HTTPS-kommunikation obligatorisk for alle sessionsbaserede apps
  • Opret cookies, der har sikre og kun HTTP-attributter

Hvorfor sessionsbaseret godkendelse?

Sessionsbaseret godkendelse er mere sikker end de fleste godkendelsesmetoder, fordi den er enkel, sikker og har en begrænset lagerstørrelse. Det menes også at være den bedste mulighed for websteder i det samme roddomæne.

Projektopsætning

Start din projektopsætning ved at installere Nest CLI globalt. Du behøver ikke at gøre dette, hvis du allerede har NestJS CLI installeret.

Nest CLI er et kommandolinjeværktøj til opsætning, udvikling og vedligeholdelse af Nest-applikationer.

npm i -g @nestjs/cli 

Lad os nu konfigurere dit projekt ved at køre følgende kommando:

nest new session-based-auth 

Ovenstående kommando opretter en Nest-applikation med nogle kedelplader og beder dig derefter om at vælge din foretrukne pakkeadministrator for at installere de nødvendige moduler for at køre din applikation. Til demonstration bruger denne selvstudie npm . Tryk på Enter-tasten for at fortsætte med npm .

Hvis alt gik godt, skulle du se et output som det på skærmbilledet nedenfor på din terminal.

Når installationen er fuldført, skal du flytte ind i din projektmappe og køre programmet med kommandoen nedenfor:

npm run start:dev 

Ovenstående kommando kører programmet og holder øje med ændringer. Dit projekt src mappestrukturen skal se ud som følger.

└───src│ └───app.controller.ts│ └───app.modules.ts│ └───app.service.ts──s.

Installer afhængigheder

Nu hvor din applikation er konfigureret, lad os installere de nødvendige afhængigheder.

npm install --save @nestjs/passport passport passport-local 

Ovenstående kommando installerer Passport.js, et populært nest.js-godkendelsesbibliotek.

Installer også typerne til strategien med kommandoen nedenfor:

Den indeholder typedefinitioner for passport-local .

npm install --save-dev @types/passport-local 

Konfigurer MongoDB-database i NestJS

For at konfigurere og forbinde din database skal du installere Mongoose-pakken og NestJS-indpakningen med følgende kommando:

npm install --save @nestjs/mongoose mongoose 

Mongoose NestJS-indpakningen hjælper dig med at bruge Mongoose i NestJS-applikationen og giver godkendt TypeScript-understøttelse.

Gå nu over til din app.module.ts , og importer mongoose modul fra @nestjs/mongoose . Kald derefter forRoot() metode, en metode leveret af Mongoose-modulet, og send din databases URL-streng.

Opsætning af din databaseforbindelse i app.module.ts hjælper din applikation med at oprette forbindelse til databasen med det samme, når serveren starter - efter at have kørt din applikation, da det er det første modul, der indlæses.

app.module.ts

importer { Module } fra "@nestjs/common"import { MongooseModule } fra "@nestjs/mongoose"import { AppController } fra "./app.controller"import { AppService } fra "./app. service"@Module({ importerer:[ MongooseModule.forRoot( "mongodb+srv://:@cluster0.kngtf.mongodb.net/session-auth?retryWrites=true&w=majority" ), ], controllere:[AppController], udbydere:[AppService],}) eksportklasse AppModule {} 

Opret brugermodul

Af hensyn til adskillelse, for at gøre din kode ren og velorganiseret, skal du oprette et modul specifikt til brugere, der bruger NestJS CLI ved at køre følgende kommando:

nest g-modulbrugere 

Ovenstående kommando opretter en brugere mappe med users.module.ts og opdaterer app.module.ts

Opret også users.service.ts og users.controller.ts filer med følgende kommandoer:

nest g service usersnest g controller-brugere 

Bemærk, at du kan oprette dine mapper og filer manuelt uden at bruge nest CLI, men brug af CLI opdaterer automatisk de nødvendige mapper og gør dit liv lettere.

Opret brugerskema

Det næste trin er at oprette dit UserSchema, men først tilføje en users.model.ts fil, hvor du vil oprette UserSchema

Dette skulle være formen på vores applikation src mappe nu.

└───src│ └───users│ │ └───users.controller.ts│ │ └───users.model.ts──s.ts─│ │ │ └───users.service.ts│ └───app.controller.ts│ └───app.modul.ts│ └──────i /kode>

For at oprette UserSchema , importer alt som mongoose fra mongoose-pakken i users.model.ts . Kald derefter det nye mongoose-skema, en blueprint af brugermodellen, og indsend et JavaScript-objekt, hvor du vil definere brugerobjektet og dataene.

users.model.ts

import * som mongoose fra "mongoose"export const UserSchema =new mongoose.Schema( { username:{ type:String, required:true, unique:true, }, password:{ type:String, required:true, }, }, { timestamps:true }) eksportgrænseflade Bruger udvider mongoose.Document { _id:string; brugernavn:streng; adgangskode:string;} 

Opret også en grænseflade til din model, der udvider mongoose, et dokument, der hjælper dig med at udfylde dine MongoDB-samlinger.

Gå over til din users.module.ts og importer MongooseModule i import-arrayet. Kald derefter forFeature() metode leveret af MongooseModule , og indsend en række objekter, der tager navn og skema ind.

Dette vil gøre dig i stand til at dele filen hvor som helst ved hjælp af afhængighedsindsprøjtning.

users.module.ts

importer { Module } fra "@nestjs/common"import { MongooseModule } fra "@nestjs/mongoose"import { UsersController } fra "./users.controller"import { UserSchema } fra "./users. model"import { UsersService } fra "./users.service"@Module({ imports:[MongooseModule.forFeature([{ navn:"user", skema:UserSchema }])], controllere:[UsersController], udbydere:[ UsersService],}) eksportklasse UsersModule {} 

I users.module.ts , eksporter UsersService for at give dig adgang til det i et andet modul.

users.module.ts

importer { Module } fra "@nestjs/common"import { MongooseModule } fra "@nestjs/mongoose"import { UsersController } fra "./users.controller"import { UserSchema } fra "./users. model"import { UsersService } fra "./users.service"@Module({ imports:[MongooseModule.forFeature([{ navn:"user", skema:UserSchema }])], controllere:[UsersController], udbydere:[ UsersService], eksporterer:[UsersService],})eksportklasse UsersModule {} 

Det er normalt en god idé at indkapsle forretningslogikken i en separat klasse. Sådan en klasse er kendt som en service. Denne klasses opgave er at behandle controllerens anmodninger og udføre forretningslogikken.

I users.service.ts fil, importer Model fra mongoose , Bruger fra users.model.ts og InjectModel fra @nestjs/mongoose . Tilføj derefter en metode til UsersService klasse, der tager et brugernavn og en adgangskode, og kalder metoden insertUser() .

users.service.ts

import { Injectable } from '@nestjs/common';import { InjectModel } from '@nestjs/mongoose';import { Model } from 'mongoose';import { User } from './users.model ';@Injectable()export class UsersService { constructor(@InjectModel('user') private readonly userModel:Model) {} async insertUser(brugernavn:streng, adgangskode:streng) { const brugernavn =brugernavn.toLowerCase(); const newUser =new this.userModel({ brugernavn, adgangskode, }); vent newUser.save(); returnere nyBruger; }} 

Nu hvor UsersService klasse er klar, skal du injicere den i din controller. Men først, lad os tale om at opbevare brugernes adgangskoder sikkert.

Det mest kritiske aspekt af registreringsproceduren er brugernes adgangskoder, som ikke må gemmes i almindelig tekst. Det er brugerens ansvar at oprette en stærk adgangskode, men det er din forpligtelse som udvikler at holde deres adgangskoder sikre. Hvis der opstår et databasebrud, vil brugernes adgangskoder blive afsløret. Og hvad sker der, hvis det er gemt i almindelig tekst? Jeg tror, ​​du kender svaret. For at løse dette, hash adgangskoden ved hjælp af bcrypt.

Så installer bcrypt og @types/bcrypt med følgende kommando:

npm installer @types/bcrypt bcrypt 

Med det af vejen, konfigurer din controller. Først skal du importere din UsersService klasse og alt fra bcrypt . Tilføj derefter en konstruktør og en metode, der giver dig mulighed for at tilføje en bruger; det vil håndtere indgående postanmodninger, kald det addUser , med en funktionstekst, hvor du hash adgangskoden.

users.controller.ts

importer { Body, Controller, Post } fra '@nestjs/common';import { UsersService } fra './users.service';import * som bcrypt fra 'bcrypt';@Controller('users' )export class UsersController { constructor(private readonly usersService:UsersService) {} //post / signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body('brugernavn') brugernavn :streng, ) { const saltEllerRunder =10; const hashedPassword =afvent bcrypt.hash(brugerPassword, saltEllerRunder); const result =afvent denne.usersService.insertUser( brugernavn, hashedPassword, ); return { msg:'Bruger registreret med succes', bruger-id:resultat.id, brugernavn:resultat.brugernavn }; }} 

Registreringen sker i app.module.ts fil, som opnås ved at tilføje UsersModule til @Module() decorators imports' array i app.module.ts .

app.module.ts

importer { Module } fra "@nestjs/common"import { MongooseModule } fra "@nestjs/mongoose"import { AppController } fra "./app.controller"import { AppService } fra "./app. service"import { UsersModule } fra "./users/users.module"@Module({ imports:[ MongooseModule.forRoot( "mongodb+srv://:@cluster0.kngtf.mongodb.net/ session-auth?retryWrites=true&w=majority" ), UsersModule, ], controllere:[AppController], udbydere:[AppService],})eksportklasse AppModule {} 

Tillykke! Du er færdig med tilmeldingen. Du kan nu registrere en bruger med et brugernavn og en adgangskode.

Nu, med registrering af vejen, tilføje en getUser funktion til din UsersService med findOne metode til at finde en bruger efter brugernavn.

users.service.ts

import { Injectable } from '@nestjs/common';import { InjectModel } from '@nestjs/mongoose';import { Model } from 'mongoose';import { User } from './users.model ';@Injectable()export class UsersService { constructor(@InjectModel('user') private readonly userModel:Model) {} async insertUser(brugernavn:streng, adgangskode:streng) { const brugernavn =brugernavn.toLowerCase(); const newUser =new this.userModel({ brugernavn, adgangskode, }); vent newUser.save(); returnere nyBruger; } async getUser(brugernavn:streng) { const brugernavn =brugernavn.toLowerCase(); const bruger =afvent denne.userModel.findOne({ brugernavn }); returnere bruger; }} 

Opret godkendelsesmodul

Ligesom for brugere skal du oprette et godkendelsesmodul og en service specifikt til alle godkendelser/verifikationer. For at gøre det skal du køre følgende kommandoer:

nest g modul authnest g service auth 

Ovenstående vil oprette en godkendelsesmappe, auth.module.ts , og auth.service.ts , og opdater auth.module.ts og app.module.ts filer.

På dette tidspunkt er formen på din applikation src mappe skal se ud som følger.

└───src│ └───auth│ │ └───auth.modul.ts│ │ └────auth.us.ts│ers── ─users.controller.ts│ │ └───users.model.ts│ │ └───users.modul.ts│ │ └───users.app.ts│s. └───app.module.ts│ └───app.service.ts│ └───main.ts

Ovenstående genereringskommando vil opdatere din app.module.ts , og det vil se ud som kodestykket nedenfor:

app.module.ts

importer { Module } fra "@nestjs/common"import { MongooseModule } fra "@nestjs/mongoose"import { AppController } fra "./app.controller"import { AppService } fra "./app. service"import { UsersModule } fra "./users/users.module"import { AuthModule } fra './auth/auth.module';@Module({ imports:[UsersModule, AuthModule, MongooseModule.forRoot( //database url streng 'mongodb://localhost:27017/myapp' )], controllere:[AppController], udbydere:[AppService],})eksportklasse AppModule {} 

Godkend brugere

Gå til din auth.module.ts fil og tilføj UsersModule i importarrayet for at give adgang til UsersService eksporteret fra users.module.ts fil.

auth.module.ts

importer { Module } fra "@nestjs/common"import { UsersModule } fra "src/users/users.module"import { AuthService } fra "./auth.service"@Module({ imports:[ UsersModule], udbydere:[AuthService],})eksportklasse AuthModule {} 

I din auth.service.ts fil, skal du kalde konstruktøren, så du kan injicere UsersService , og tilføj en metode til validering, der vil tage et brugernavn og en adgangskode.

For at tilføje nogle grundlæggende valideringer skal du kontrollere, om brugeren findes i databasen, og sammenligne den givne adgangskode med den i din database for at sikre, at den matcher. Hvis det findes, skal du returnere brugeren i request.user objekt — ellers returner null.

auth.service.ts

 importer { Injectable, NotAcceptableException } fra '@nestjs/common'; importer { UsersService } fra 'src/users/users.service'; import * som bcrypt fra 'bcrypt'; @Injectable() eksportklasse AuthService { constructor(private readonly usersService:UsersService) {} async validateUser(brugernavn:string, password:string):Promise { const user =await this.usersService.getUser(brugernavn); const passwordValid =afvent bcrypt.compare(adgangskode, bruger.adgangskode) hvis (!bruger) { throw new NotAcceptableException('kunne ikke finde brugeren'); } if (bruger &&passwordValid) { return { userId:user.id, userName:user.username }; } returner null; } } 

Gå videre, opret en ny fil og navngiv den local.strategy.ts . Denne fil vil repræsentere strategien fra Passport.js , som du installerede tidligere, det er den lokale strategi . Og inden for den skal du videregive strategien, som er Strategien fra passport-local .

Opret en konstruktør og injicer AuthService , kald super() metode; sørg for at kalde super() metode.

local.strategy.ts

 importer { Injectable, UnauthorizedException } fra '@nestjs/common'; importer { PassportStrategy } fra '@nestjs/passport'; importere { Strategi } fra 'pas-lokal'; importer { AuthService } fra './auth.service'; @Injectable() eksportklasse LocalStrategy udvider PassportStrategy(Strategy) { constructor(private readonly authService:AuthService) { super(); } async validate(brugernavn:streng, adgangskode:streng):Promise { const brugernavn =brugernavn.toLowerCase(); const bruger =afvent denne.authService.validateUser(brugernavn, adgangskode); if (!bruger) { throw new UnauthorizedException(); } returnere bruger; } } 

Gå tilbage til din auth.module.ts fil. Tilføj derefter PassportModule til importer og LocalStrategy til udbydere.

auth.module.ts

importer { Module } fra "@nestjs/common"import { PassportModule } fra "@nestjs/passport"import { UsersModule } fra "src/users/users.module"import { AuthService } fra "./ auth.service"import { LocalStrategy } fra "./local.strategy"@Module({ importer:[UsersModule, PassportModule], udbydere:[AuthService, LocalStrategy],})eksportklasse AuthModule {} 

Tilføj nu login-ruten til din users.controller.ts :

users.controller.ts

 importer { Body, Controller, Post, Request, } fra '@nestjs/common'; import * som bcrypt fra 'bcrypt'; importer { UsersService } fra './users.service'; @Controller('brugere') eksportklasse UsersController { constructor(private readonly usersService:UsersService) {} //post / signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body ('brugernavn') brugernavn:streng, ) { const saltOrRounds =10; const hashedPassword =afvent bcrypt.hash(brugerPassword, saltEllerRunder); const result =afvent denne.usersService.insertUser( brugernavn, hashedPassword, ); return { msg:'Bruger registreret med succes', bruger-id:resultat.id, brugernavn:resultat.brugernavn }; } //Post / Login @Post('/login') login(@Request() req):enhver { return {Bruger:req.user, msg:'Bruger logget ind'}; } } 

Nu hvor du har alle disse på plads, kan du stadig ikke logge på en bruger, fordi der ikke er noget, der udløser login-ruten. Brug her Guards til at opnå det.

Opret en fil og navngiv den local.auth.guard.ts , derefter en klasse LocalAuthGuard der udvider AuthGuard fra NestJS/pas , hvor du angiver navnet på strategien og sender navnet på din strategi, local .

local.auth.guard.ts.

importer { Injectable } fra "@nestjs/common"import { AuthGuard } fra "@nestjs/passport"@Injectable()export class LocalAuthGuard udvider AuthGuard("local") {} 

Tilføj UseGuard dekorator til din login-rute i users.controller.ts fil, og indsend LocalAuthGuard .

users.controller.ts

 importer { Body, Controller, Post, UseGuards, Request, } fra '@nestjs/common'; import * som bcrypt fra 'bcrypt'; importer { LocalAuthGuard } fra 'src/auth/local.auth.guard'; importer { UsersService } fra './users.service'; @Controller('brugere') eksportklasse UsersController { constructor(private readonly usersService:UsersService) {} //post / signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body ('brugernavn') brugernavn:streng, ) { const saltOrRounds =10; const hashedPassword =afvent bcrypt.hash(brugerPassword, saltEllerRunder); const result =afvent denne.usersService.insertUser( brugernavn, hashedPassword, ); return { msg:'Bruger registreret med succes', bruger-id:resultat.id, brugernavn:resultat.brugernavn }; } //Post / Login @UseGuards(LocalAuthGuard) @Post('/login') login(@Request() req):enhver { return {Bruger:req.user, msg:'Bruger logget ind'}; } } 

Endelig kan du logge på en bruger med et registreret brugernavn og adgangskode.

Beskyt godkendelsesruter

Du har konfigureret brugergodkendelse. Beskyt nu dine ruter mod uautoriseret adgang ved at begrænse adgangen til kun godkendte brugere. Gå til din users.controller.ts fil, og tilføj en anden rute - navngiv den 'beskyttet' og få den til at returnere req.user objekt.

users.controller.ts

 importer { Body, Controller, Get, Post, UseGuards, Request, } fra '@nestjs/common'; import * som bcrypt fra 'bcrypt'; importer { LocalAuthGuard } fra 'src/auth/local.auth.guard'; importer { UsersService } fra './users.service'; @Controller('brugere') eksportklasse UsersController { constructor(private readonly usersService:UsersService) {} //signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body(' brugernavn') brugernavn:streng, ) { const saltOrRounds =10; const hashedPassword =afvent bcrypt.hash(brugerPassword, saltEllerRunder); const result =afvent denne.usersService.insertUser( brugernavn, hashedPassword, ); return { msg:'Bruger registreret med succes', bruger-id:resultat.id, brugernavn:resultat.brugernavn }; } //Post / Login @UseGuards(LocalAuthGuard) @Post('/login') login(@Request() req):enhver { return {Bruger:req.user, msg:'Bruger logget ind'}; } // Get / protected @Get('/protected') getHello(@Request() req):string { return req.user; } } 

Den beskyttede rute i ovenstående kode vil returnere et tomt objekt i stedet for at returnere brugerens detaljer, når en logget ind bruger sender en anmodning til den, fordi den allerede har mistet login.

For at få det sorteret er det her den sessionsbaserede godkendelse kommer ind.

I sessionsbaseret autentificering, når en bruger logger på, gemmes brugeren i en session, så enhver efterfølgende anmodning fra brugeren efter login vil gribe detaljerne fra sessionen og give brugeren nem adgang. Sessionen udløber, når brugeren logger ud.

For at starte sessionsbaseret godkendelse skal du installere express-session og NestJS-typerne ved hjælp af følgende kommando:

npm installer express-session @types/express-session 

Når installationen er fuldført, skal du gå til din main.ts fil, roden af ​​din applikation, og lav konfigurationerne der.

Importer alt fra pas og ekspress-session , og tilføj derefter pasinitialisering og passession.

Det er at foretrække at beholde din hemmelige nøgle i dine miljøvariabler.

main.ts

importer { NestFactory } fra "@nestjs/core"import { AppModule } fra "./app.module"import * som session fra "express-session"import * som pas fra "passport"async funktion bootstrap () { const app =await NestFactory.create(AppModule) app.use( session({ secret:"keyboard", resave:false, saveUninitialized:false, }) ) app.use(passport.initialize()) app.use (passport.session()) await app.listen(3000)}bootstrap() 

Tilføj en ny fil, authenticated.guard.ts , i din auth folder. Og opret en ny Guard, der kontrollerer, om der er en session for brugeren, der foretager anmodningen - giv den navnet authenticatedGuard .

authenticated.guard.ts

importer { CanActivate, ExecutionContext, Injectable } fra "@nestjs/common"@Injectable()export class AuthenticatedGuard implementerer CanActivate { async canActivate(context:ExecutionContext) { const request =context.switchToHttp().getRequest().getRequest(). ) returner request.isAuthenticated() }} 

I ovenstående kode hentes anmodningen fra konteksten og kontrolleres, hvis den er godkendt. isAuthenticated() kommer fra passport.js automatisk; det siger. "hey! findes der en session for denne bruger? Hvis ja, fortsæt."

For at udløse login, i din users.controller.ts fil:

  • importer godkendt fra authenticated.guard.ts;
  • tilføj useGuard dekorator til den beskyttede rute; og,
  • indtast AuthenticatedGuard .

users.controller.ts

 importer { Body, Controller, Get, Post, UseGuards, Request, } fra '@nestjs/common'; import * som bcrypt fra 'bcrypt'; importer { AuthenticatedGuard } fra 'src/auth/authenticated.guard'; importer { LocalAuthGuard } fra 'src/auth/local.auth.guard'; importer { UsersService } fra './users.service'; @Controller('brugere') eksportklasse UsersController { constructor(private readonly usersService:UsersService) {} //signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body(' brugernavn') brugernavn:streng, ) { const saltOrRounds =10; const hashedPassword =afvent bcrypt.hash(brugerPassword, saltEllerRunder); const result =afvent denne.usersService.insertUser( brugernavn, hashedPassword, ); return { msg:'Bruger registreret med succes', bruger-id:resultat.id, brugernavn:resultat.brugernavn }; } //Post / Login @UseGuards(LocalAuthGuard) @Post('/login') login(@Request() req):enhver { return {Bruger:req.user, msg:'Bruger logget ind'}; } //Get / protected @UseGuards(AuthenticatedGuard) @Get('/protected') getHello(@Request() req):string { return req.user; } } 

På dette tidspunkt mislykkes det stadig, fordi du kun har konfigureret express-session men implementerede det ikke.

Når en bruger logger på, skal du gemme brugeren i en session, så brugeren kan få adgang til andre ruter med sessionen.

En ting at huske på er, at ekspress-sessionen som standard er biblioteket gemmer sessionen i webserverens hukommelse.

Før det går ind i sessionen, skal du serialisere brugeren. Når det kommer ud af sessionen, skal du deserialisere brugeren.

Så opret en ny fil i godkendelsesmappen til serializer og deserializer, navngiv den session.serializer.ts .

På dette tidspunkt er formen på vores applikation src mappen skulle se sådan ud.

└───src │ └───auth │ │ └───auth.module.ts │ │ └────auth.ts.ts. │ │ └───local.auth.guard.ts │ │ └───local.strategy.ts │ │ └───session.serializer.ts ─ers─er .ts │ │ └───users.model.ts │ │ └───users.modul.ts │ │ └───users.service.ts ──s. app.modul.ts │ └───app.service.ts │ └───main.ts

session.serializer.ts

importer { Injectable } fra "@nestjs/common"import { PassportSerializer } fra "@nestjs/passport"@Injectable()export class SessionSerializer udvider PassportSerializer { serializeUser(bruger:enhver, udført:(err:Error) , bruger:enhver) => void):enhver { done(null, user) } deserializeUser( payload:any, done:(err:Error, payload:string) => void ):any { done(null, payload) } } 

Gå tilbage til din auth.module.ts fil, skal du angive SessionSerializer , og tilføj registret metode til PassportModule .

auth.module.ts

importer { Module } fra "@nestjs/common"import { PassportModule } fra "@nestjs/passport"import { UsersModule } fra "src/users/users.module"import { AuthService } fra "./ auth.service"import { LocalStrategy } fra "./local.strategy"import { SessionSerializer } fra "./session.serializer"@Module({ imports:[UsersModule, PassportModule.register({ session:true })], udbydere :[AuthService, LocalStrategy, SessionSerializer],})eksportklasse AuthModule {} 

Add some codes within the LocalAuthGuard in the local.auth.guard.ts file.

Call the login method in super and pass in the request to trigger the actual login by creating a session. If you want to use sessions, you must remember to trigger the super.login() .

local.auth.guard.ts

 import { ExecutionContext, Injectable } from '@nestjs/common'; import { AuthGuard } from '@nestjs/passport'; @Injectable() export class LocalAuthGuard extends AuthGuard('local') { async canActivate(context:ExecutionContext) { const result =(await super.canActivate(context)) as boolean; const request =context.switchToHttp().getRequest(); await super.logIn(request); return result; } } 

If you log in now, you will see the session ID stored in a cookie, which is just a key to the session store, and the cookie gets saved in the browser. The cookie is automatically attached to the rest of the request.

Now that the session is working, you can access the protected route; it will return the expected user’s details.

Logout Users

As mentioned earlier, once a user logs out, you destroy all sessions.

To log out a user, go to the users.controller.ts file, add a logout route, and call the req.session.session() method. You can return a message notifying that the user’s session has ended.

 import { Body, Controller, Get, Post, UseGuards, Request, } from '@nestjs/common'; import * as bcrypt from 'bcrypt'; import { AuthenticatedGuard } from 'src/auth/authenticated.guard'; import { LocalAuthGuard } from 'src/auth/local.auth.guard'; import { UsersService } from './users.service'; @Controller('users') export class UsersController { constructor(private readonly usersService:UsersService) {} //signup @Post('/signup') async addUser( @Body('password') userPassword:string, @Body('username') userName:string, ) { const saltOrRounds =10; const hashedPassword =await bcrypt.hash(userPassword, saltOrRounds); const result =await this.usersService.insertUser( userName, hashedPassword, ); return { msg:'User successfully registered', userId:result.id, userName:result.username }; } //Post / Login @UseGuards(LocalAuthGuard) @Post('/login') login(@Request() req):any { return {User:req.user, msg:'User logged in'}; } //Get / protected @UseGuards(AuthenticatedGuard) @Get('/protected') getHello(@Request() req):string { return req.user; } //Get / logout @Get('/logout') logout(@Request() req):any { req.session.destroy(); return { msg:'The user session has ended' } } } 

So, once you log out, it returns a message notifying you that the user session has ended. The code for this tutorial is hosted here on my Github repository.

Test Your Application

You have successfully implemented user signup, authentication, and protected the route to enable authorized access only.

It’s time to test the application. If everything is in order, your server should be running. Else, restart your server with the following command:

npm run start:dev 

Head over to your Postman. And let’s finally test our application.

Sign Up As a User

Log In As a User

Request the Protected Route

User Logout

Alternatively, Implement User Authentication with LoginRadius

LoginRadius provides a variety of registration and authentication services to assist you in better connecting with your consumers.

On any web or mobile application, LoginRadius is the developer-friendly Identity Platform that delivers a complete set of APIs for authentication, identity verification, single sign-on, user management, and account protection capabilities like multi-factor authentication.

To implement LoginRadius in your NestJS application, follow this tutorial:NestJS User Authentication with LoginRadius API.

Conclusion

Tillykke! In this tutorial, you've learned how to implement session-based authentication in a NestJS application with the MongoDB database. You've created and authenticated a user and protected your routes from unauthorized access.

You can access the sample code used in this tutorial on GitHub.

Bemærk: Session storage is saved by default in 'MemoryStore,' which is not intended for production use. So, while no external datastore is required for development, once in production, a data store such as Redis or another is suggested for stability and performance. You can learn more about session storage here.


  1. MongoDB database skema design

  2. Hvad er den anbefalede ækvivalent af kaskadedelt sletning i MongoDB for N:M-relationer?

  3. Hvordan bruger jeg Node.js-klynger med min simple Express-app?

  4. Hvordan implementerer man redis' pubsub timeout-funktion?