MongoDB
 sql >> Teknologi Basis Data >  >> NoSQL >> MongoDB

NestJS:Cara Menerapkan Otentikasi Pengguna Berbasis Sesi

Pengantar

Ini adalah kenyataan yang tak terbantahkan bahwa otentikasi sangat penting dalam aplikasi atau sistem apa pun jika Anda ingin mengamankan data pengguna dan memungkinkan akses aman ke informasi. Otentikasi adalah prosedur untuk menetapkan atau menunjukkan bahwa sesuatu itu benar, sah, atau valid.

Prasyarat

Tutorial ini adalah demonstrasi langsung. Untuk mengikuti, pastikan Anda memiliki yang berikut ini:

  • Node.js berjalan di sistem Anda karena NestJS adalah framework Node.js
  • MongoDB terpasang

Apa itu NestJS?

Nest (NestJS) adalah framework aplikasi sisi server Node.js untuk membangun aplikasi yang skalabel dan efisien.

Itu ditulis dalam TypeScript dan dibangun di atas Express, kerangka kerja yang sangat minimalis yang hebat dengan sendirinya tetapi tidak memiliki struktur. Ini menggabungkan paradigma pemrograman seperti pemrograman berorientasi objek, pemrograman fungsional, dan pemrograman reaktif fungsional.

Ini adalah kerangka kerja untuk digunakan jika Anda menginginkan banyak struktur di backend Anda. Sintaks dan strukturnya sangat mirip dengan AngularJS, kerangka kerja front-end. Dan ia menggunakan TypeScript, layanan, dan injeksi ketergantungan dengan cara yang sama seperti AngularJS.

Ini menggunakan modul dan pengontrol, dan Anda dapat membuat pengontrol untuk file menggunakan antarmuka baris perintah.

Modul NestJS memungkinkan Anda untuk mengelompokkan pengontrol dan penyedia layanan terkait ke dalam satu file kode. Sederhananya, modul NestJS adalah file TypeScript dengan @Module anotasi (). Dekorator ini menginformasikan framework NestJS tentang pengontrol, penyedia layanan, dan sumber daya terkait lainnya yang akan dibuat dan digunakan oleh kode aplikasi nanti.

Apa itu Otentikasi Berbasis Sesi?

Otentikasi berbasis sesi adalah metode otentikasi pengguna di mana server membuat sesi setelah login berhasil, dengan ID sesi disimpan dalam cookie atau penyimpanan lokal di browser Anda.

Atas permintaan berikutnya, cookie Anda divalidasi terhadap ID sesi yang disimpan di server. Jika ada kecocokan, permintaan dianggap sah dan diproses.

Saat menggunakan metode autentikasi ini, penting untuk mengingat praktik terbaik keamanan berikut:

  • Buat ID sesi yang panjang dan acak (128 bit adalah panjang yang disarankan) untuk membuat serangan brute force tidak efektif
  • Hindari menyimpan data sensitif atau khusus pengguna
  • Jadikan komunikasi HTTPS wajib untuk semua aplikasi berbasis sesi
  • Buat cookie yang memiliki atribut aman dan khusus HTTP

Mengapa Otentikasi Berbasis Sesi?

Otentikasi berbasis sesi lebih aman daripada kebanyakan metode otentikasi karena sederhana, aman, dan memiliki ukuran penyimpanan terbatas. Ini juga dianggap sebagai opsi terbaik untuk situs web di domain root yang sama.

Penyiapan Proyek

Mulai penyiapan proyek Anda dengan menginstal Nest CLI secara global. Anda tidak perlu melakukan ini jika Anda sudah menginstal NestJS CLI.

Nest CLI adalah alat antarmuka baris perintah untuk menyiapkan, mengembangkan, dan memelihara aplikasi Nest.

npm i -g @nestjs/cli

Sekarang, mari siapkan proyek Anda dengan menjalankan perintah berikut:

nest new session-based-auth

Perintah di atas membuat aplikasi Nest dengan beberapa boilerplate, kemudian meminta Anda untuk memilih manajer paket pilihan Anda untuk menginstal modul yang diperlukan untuk menjalankan aplikasi Anda. Untuk demonstrasi, tutorial ini menggunakan npm . Tekan tombol enter untuk melanjutkan dengan npm .

Jika semuanya berjalan dengan baik, Anda akan melihat output seperti pada tangkapan layar di bawah di terminal Anda.

Setelah instalasi selesai, pindah ke direktori proyek Anda, dan jalankan aplikasi dengan perintah di bawah ini:

npm run start:dev

Perintah di atas menjalankan aplikasi dan melihat perubahan. src proyek Anda struktur folder akan terlihat sebagai berikut.

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

Instal Dependensi

Sekarang setelah aplikasi Anda siap, mari instal dependensi yang diperlukan.

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

Perintah di atas menginstal Passport.js, library otentikasi nest.js yang populer.

Juga, instal jenis strategi dengan perintah di bawah ini:

Ini berisi definisi tipe untuk passport-local .

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

Menyiapkan Basis Data MongoDB di NestJS

Untuk menyiapkan dan menghubungkan database Anda, instal paket Mongoose dan pembungkus NestJS dengan perintah berikut:

npm install --save @nestjs/mongoose mongoose

Pembungkus NestJS Mongoose membantu Anda menggunakan Mongoose di aplikasi NestJS dan memberikan dukungan TypeScript yang disetujui.

Sekarang, buka app.module.ts Anda , dan impor mongoose modul dari @nestjs/mongoose . Kemudian panggil forRoot() metode, metode yang disediakan oleh modul Mongoose, dan meneruskan string URL database Anda.

Menyiapkan koneksi database Anda di app.module.ts membantu aplikasi Anda terhubung ke database segera saat server dimulai — setelah menjalankan aplikasi Anda karena ini adalah modul pertama yang dimuat.

app.module.ts

import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"

@Module({
  imports: [
    MongooseModule.forRoot(
      "mongodb+srv://<username>:<password>@cluster0.kngtf.mongodb.net/session-auth?retryWrites=true&w=majority"
    ),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Buat Modul Pengguna

Untuk masalah pemisahan, untuk membuat kode Anda bersih dan tertata dengan baik, buat modul khusus untuk pengguna yang menggunakan NestJS CLI dengan menjalankan perintah berikut:

nest g module users

Perintah di atas membuat users folder dengan users.module.ts dan memperbarui app.module.ts

Juga, buat users.service.ts dan users.controller.ts file dengan perintah berikut:

nest g service users
nest g controller users

Perhatikan bahwa Anda dapat membuat folder dan file secara manual tanpa menggunakan CLI bersarang, tetapi menggunakan CLI secara otomatis memperbarui folder yang diperlukan dan membuat hidup Anda lebih mudah.

Buat Skema Pengguna

Langkah selanjutnya adalah membuat UserSchema Anda, tetapi pertama-tama, tambahkan users.model.ts file, di mana Anda akan membuat UserSchema

Ini harus menjadi bentuk aplikasi kita src folder sekarang.

└───src
│   └───users
│   │   └───users.controller.ts
│   │   └───users.model.ts
│   │   └───users.module.ts
│   │   └───users.service.ts
│   └───app.controller.ts
│   └───app.module.ts
│   └───app.service.ts
│   └───main.ts

Untuk membuat UserSchema , impor semuanya sebagai luwak dari paket luwak di users.model.ts . Kemudian panggil skema luwak baru, cetak biru Model pengguna, dan berikan objek JavaScript tempat Anda akan mendefinisikan objek dan data pengguna.

users.model.ts

import * as mongoose from "mongoose"
export const UserSchema = new mongoose.Schema(
  {
    username: {
      type: String,
      required: true,
      unique: true,
    },
    password: {
      type: String,
      required: true,
    },
  },
  { timestamps: true }
)

export interface User extends mongoose.Document {
  _id: string;
  username: string;
  password: string;
}

Juga, buat antarmuka untuk Model Anda yang memperluas luwak, dokumen yang membantu Anda mengisi koleksi MongoDB Anda.

Buka users.module.ts Anda dan impor MongooseModule dalam larik impor. Kemudian panggil forFeature() metode yang disediakan oleh MongooseModule , dan meneruskan array objek yang mengambil nama dan skema.

Ini akan memungkinkan Anda untuk berbagi file di mana saja dengan bantuan injeksi ketergantungan.

users.module.ts

import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { UsersController } from "./users.controller"
import { UserSchema } from "./users.model"
import { UsersService } from "./users.service"
@Module({
  imports: [MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
  controllers: [UsersController],
  providers: [UsersService],
})
export class UsersModule {}

Di users.module.ts , ekspor UsersService untuk memungkinkan Anda mengaksesnya di modul lain.

users.module.ts

import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { UsersController } from "./users.controller"
import { UserSchema } from "./users.model"
import { UsersService } from "./users.service"
@Module({
  imports: [MongooseModule.forFeature([{ name: "user", schema: UserSchema }])],
  controllers: [UsersController],
  providers: [UsersService],
  exports: [UsersService],
})
export class UsersModule {}

Biasanya ide yang baik untuk merangkum logika bisnis di kelas yang terpisah. Kelas seperti itu dikenal sebagai layanan. Tugas kelas ini adalah memproses permintaan pengontrol dan menjalankan logika bisnis.

Di users.service.ts file, impor Model dari mongoose , User dari users.model.ts , dan InjectModel dari @nestjs/mongoose . Kemudian tambahkan metode ke UsersService kelas yang mengambil nama pengguna dan kata sandi, dan memanggil metode 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<User>) {}
  async insertUser(userName: string, password: string) {
    const username = userName.toLowerCase();
    const newUser = new this.userModel({
      username,
      password,
    });
    await newUser.save();
    return newUser;
  }
}

Sekarang UsersService kelas sudah siap, Anda perlu menyuntikkannya ke controller Anda. Tapi pertama-tama, mari kita bicara tentang menyimpan kata sandi pengguna dengan aman.

Aspek paling kritis dari prosedur pendaftaran adalah kata sandi pengguna, yang tidak boleh disimpan dalam teks biasa. Merupakan tanggung jawab pengguna untuk membuat kata sandi yang kuat, tetapi adalah kewajiban Anda sebagai pengembang untuk menjaga keamanan kata sandi mereka. Jika pelanggaran basis data terjadi, kata sandi pengguna akan terungkap. Dan apa yang terjadi jika disimpan dalam teks biasa? Saya percaya Anda tahu jawabannya. Untuk mengatasinya, hash kata sandi menggunakan bcrypt.

Jadi, instal bcrypt dan @types/bcrypt dengan perintah berikut:

npm install @types/bcrypt bcrypt

Dengan itu, atur pengontrol Anda. Pertama, impor UsersService Anda kelas dan semuanya dari bcrypt . Kemudian tambahkan konstruktor dan metode yang memungkinkan Anda menambahkan pengguna; itu akan menangani permintaan posting yang masuk, sebut saja addUser , dengan badan fungsi tempat Anda akan membuat hash sandi.

users.controller.ts

import { Body, Controller, Post } from '@nestjs/common';
import { UsersService } from './users.service';
import * as bcrypt from 'bcrypt';
@Controller('users')
export class UsersController {
  constructor(private readonly usersService: UsersService) {}
  //post / 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
    };
  }
}

Pendaftaran terjadi di app.module.ts file, yang dicapai dengan menambahkan UsersModule ke @Module() array impor dekorator di app.module.ts .

app.module.ts

import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"
import { UsersModule } from "./users/users.module"

@Module({
  imports: [
    MongooseModule.forRoot(
      "mongodb+srv://<username>:<password>@cluster0.kngtf.mongodb.net/session-auth?retryWrites=true&w=majority"
    ),
    UsersModule,
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Selamat! Anda selesai dengan pendaftaran. Anda sekarang dapat mendaftarkan pengguna dengan nama pengguna dan kata sandi.

Sekarang, tanpa pendaftaran, tambahkan getUser fungsi ke UsersService your Anda dengan findOne metode untuk menemukan pengguna berdasarkan nama pengguna.

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<User>) {}
  async insertUser(userName: string, password: string) {
    const username = userName.toLowerCase();
    const newUser = new this.userModel({
      username,
      password,
    });
    await newUser.save();
    return newUser;
  }
  async getUser(userName: string) {
    const username = userName.toLowerCase();
    const user = await this.userModel.findOne({ username });
    return user;
  }
}

Buat Modul Otentikasi

Sama seperti untuk pengguna, buat modul dan layanan autentikasi khusus untuk semua autentikasi/verifikasi. Untuk melakukannya, jalankan perintah berikut:

nest g module auth
nest g service auth

Di atas akan membuat folder auth, auth.module.ts , dan auth.service.ts , dan perbarui auth.module.ts dan app.module.ts file.

Pada titik ini, bentuk aplikasi Anda src folder akan terlihat sebagai berikut.

└───src
│   └───auth
│   │   └───auth.module.ts
│   │   └───auth.service.ts
│   └───users
│   │   └───users.controller.ts
│   │   └───users.model.ts
│   │   └───users.module.ts
│   │   └───users.service.ts
│   └───app.controller.ts
│   └───app.module.ts
│   └───app.service.ts
│   └───main.ts

Perintah generate di atas akan memperbarui app.module.ts . Anda , dan akan terlihat seperti cuplikan kode di bawah ini:

app.module.ts

import { Module } from "@nestjs/common"
import { MongooseModule } from "@nestjs/mongoose"
import { AppController } from "./app.controller"
import { AppService } from "./app.service"
import { UsersModule } from "./users/users.module"
import { AuthModule } from './auth/auth.module';


@Module({
  imports: [UsersModule, AuthModule, MongooseModule.forRoot(
    //database url string
    'mongodb://localhost:27017/myapp'
    )],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Otentikasi Pengguna

Buka auth.module.ts . Anda file dan tambahkan UsersModule dalam larik impor untuk mengaktifkan akses ke UsersService diekspor dari users.module.ts berkas.

auth.module.ts

import { Module } from "@nestjs/common"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"

@Module({
  imports: [UsersModule],
  providers: [AuthService],
})
export class AuthModule {}

Di auth.service.ts . Anda file, panggil konstruktor sehingga Anda dapat menyuntikkan UsersService , dan tambahkan metode untuk validasi yang akan menggunakan nama pengguna dan sandi.

Untuk menambahkan beberapa validasi dasar, periksa apakah pengguna ada di database, dan bandingkan kata sandi yang diberikan dengan yang ada di database Anda untuk memastikannya cocok. Jika ada, kembalikan pengguna di request.user objek — yang lain, kembalikan nol.

auth.service.ts

    import { Injectable, NotAcceptableException } from '@nestjs/common';
    import { UsersService } from 'src/users/users.service';
    import * as bcrypt from 'bcrypt';

    @Injectable()
    export class AuthService {
      constructor(private readonly usersService: UsersService) {}
      async validateUser(username: string, password: string): Promise<any> {
        const user = await this.usersService.getUser(username);
        const passwordValid = await bcrypt.compare(password, user.password)
        if (!user) {
            throw new NotAcceptableException('could not find the user');
          }
        if (user && passwordValid) {
          return {
            userId: user.id,
            userName: user.username
          };
        }
        return null;
      }
    }

Lebih jauh, buat file baru dan beri nama local.strategy.ts . File ini akan mewakili strategi dari Passport.js , yang Anda instal sebelumnya, itu adalah local strategy . Dan di dalamnya, berikan strateginya, yaitu Strategy dari passport-local .

Buat konstruktor dan masukkan AuthService , panggil super() metode; pastikan untuk memanggil super() metode.

local.strategy.ts

    import { Injectable, UnauthorizedException } from '@nestjs/common';
    import { PassportStrategy } from '@nestjs/passport';
    import { Strategy } from 'passport-local';
    import { AuthService } from './auth.service';
    @Injectable()
    export class LocalStrategy extends PassportStrategy(Strategy) {
      constructor(private readonly authService: AuthService) {
        super();
      }
      async validate(username: string, password: string): Promise<any> {
        const userName = username.toLowerCase();
        const user = await this.authService.validateUser(userName, password);
        if (!user) {
          throw new UnauthorizedException();
        }
        return user;
      }
    }

Kembali ke auth.module.ts Anda mengajukan. Kemudian tambahkan PassportModule untuk mengimpor dan LocalStrategy ke penyedia.

auth.module.ts

import { Module } from "@nestjs/common"
import { PassportModule } from "@nestjs/passport"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"
import { LocalStrategy } from "./local.strategy"

@Module({
  imports: [UsersModule, PassportModule],
  providers: [AuthService, LocalStrategy],
})
export class AuthModule {}

Sekarang, tambahkan rute login ke users.controller.ts Anda :

users.controller.ts

    import {
      Body,
      Controller,
      Post,
      Request,
    } from '@nestjs/common';
    import * as bcrypt from 'bcrypt';
    import { UsersService } from './users.service';
    @Controller('users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}
      //post / 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
      @Post('/login')
      login(@Request() req): any {
        return {User: req.user,
                msg: 'User logged in'};
      }
    }

Sekarang setelah Anda memiliki semua ini, Anda masih tidak dapat memasukkan pengguna karena tidak ada yang memicu rute masuk. Di sini, gunakan Penjaga untuk mencapai itu.

Buat file dan beri nama local.auth.guard.ts , lalu kelas LocalAuthGuard yang memperluas AuthGuard dari NestJS/passport , di mana Anda akan memberikan nama strategi dan meneruskan nama strategi Anda, local .

local.auth.guard.ts.

import { Injectable } from "@nestjs/common"
import { AuthGuard } from "@nestjs/passport"
@Injectable()
export class LocalAuthGuard extends AuthGuard("local") {}

Tambahkan UseGuard dekorator ke rute login Anda di users.controller.ts file, dan berikan LocalAuthGuard .

users.controller.ts

    import {
      Body,
      Controller,
      Post,
      UseGuards,
      Request,
    } from '@nestjs/common';
    import * as bcrypt from 'bcrypt';
    import { LocalAuthGuard } from 'src/auth/local.auth.guard';
    import { UsersService } from './users.service';
    @Controller('users')
    export class UsersController {
      constructor(private readonly usersService: UsersService) {}
      //post / 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'};
      }
    }

Terakhir, Anda dapat masuk sebagai pengguna dengan nama pengguna dan kata sandi terdaftar.

Lindungi Rute Otentikasi

Anda telah berhasil mengatur otentikasi pengguna. Sekarang, lindungi rute Anda dari akses tidak sah dengan membatasi akses hanya kepada pengguna yang diautentikasi. Buka users.controller.ts Anda file, dan tambahkan rute lain — beri nama 'dilindungi' dan buat itu mengembalikan req.user objek.

users.controller.ts

    import {
      Body,
      Controller,
      Get,
      Post,
      UseGuards,
      Request,
    } from '@nestjs/common';
    import * as bcrypt from 'bcrypt';
    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
      @Get('/protected')
      getHello(@Request() req): string {
        return req.user;
      }
    }

Rute yang dilindungi dalam kode di atas akan mengembalikan objek kosong alih-alih mengembalikan detail pengguna saat pengguna yang masuk membuat permintaan karena sudah kehilangan login.

Untuk menyortirnya, di sinilah otentikasi berbasis sesi masuk.

Dalam otentikasi berbasis sesi, ketika pengguna masuk, pengguna disimpan dalam suatu sesi sehingga permintaan berikutnya oleh pengguna setelah masuk akan mengambil detail dari sesi dan memberikan akses mudah kepada pengguna. Sesi berakhir saat pengguna logout.

Untuk memulai autentikasi berbasis sesi, instal sesi ekspres dan tipe NestJS menggunakan perintah berikut:

npm install express-session @types/express-session

Ketika instalasi selesai, buka main.ts Anda file, root aplikasi Anda, dan lakukan konfigurasi di sana.

Impor semuanya dari passport dan express-session , lalu tambahkan inisialisasi paspor dan sesi paspor.

Lebih baik menyimpan kunci rahasia Anda di variabel lingkungan Anda.

main.ts

import { NestFactory } from "@nestjs/core"
import { AppModule } from "./app.module"
import * as session from "express-session"
import * as passport from "passport"
async function 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()

Tambahkan file baru, authenticated.guard.ts , di auth . Anda map. Dan buat Guard baru yang memeriksa apakah ada sesi untuk pengguna yang membuat permintaan — beri nama authenticatedGuard .

authenticated.guard.ts

import { CanActivate, ExecutionContext, Injectable } from "@nestjs/common"

@Injectable()
export class AuthenticatedGuard implements CanActivate {
  async canActivate(context: ExecutionContext) {
    const request = context.switchToHttp().getRequest()
    return request.isAuthenticated()
  }
}

Dalam kode di atas, permintaan diperoleh dari konteks dan diperiksa jika diautentikasi. isAuthenticated() berasal dari passport.js secara otomatis; ia mengatakan. "hei! apakah ada sesi untuk pengguna ini? Jika ya, lanjutkan."

Untuk memicu login, di users.controller.ts . Anda berkas:

  • impor authenticated dari authenticated.guard.ts;
  • tambahkan useGuard dekorator ke protected rute; dan,
  • masukkan AuthenticatedGuard .

users.controller.ts

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

Pada titik ini, masih gagal karena Anda hanya mengonfigurasi express-session tetapi tidak menerapkannya.

Saat pengguna masuk, Anda perlu menyimpan pengguna dalam sebuah sesi sehingga pengguna dapat mengakses rute lain dengan sesi tersebut.

Satu hal yang perlu diingat adalah bahwa secara default, express-session library menyimpan sesi dalam memori server web.

Sebelum masuk ke sesi, Anda perlu membuat serial pengguna. Saat keluar dari sesi, deserialize pengguna.

Jadi, buat file baru di folder auth untuk serializer dan deserializer, beri nama session.serializer.ts .

Pada titik ini, bentuk aplikasi kita src foldernya akan terlihat seperti ini.

    └───src
    │   └───auth
    │   │   └───auth.module.ts
    │   │   └───auth.service.ts
    │   │   └───authenticated.guard.ts
    │   │   └───local.auth.guard.ts
    │   │   └───local.strategy.ts
    │   │   └───session.serializer.ts
    │   └───users
    │   │   └───users.controller.ts
    │   │   └───users.model.ts
    │   │   └───users.module.ts
    │   │   └───users.service.ts
    │   └───app.controller.ts
    │   └───app.module.ts
    │   └───app.service.ts
    │   └───main.ts

session.serializer.ts

import { Injectable } from "@nestjs/common"
import { PassportSerializer } from "@nestjs/passport"

@Injectable()
export class SessionSerializer extends PassportSerializer {
  serializeUser(user: any, done: (err: Error, user: any) => void): any {
    done(null, user)
  }
  deserializeUser(
    payload: any,
    done: (err: Error, payload: string) => void
  ): any {
    done(null, payload)
  }
}

Kembali ke auth.module.ts Anda file, berikan SessionSerializer , dan tambahkan register metode ke PassportModule .

auth.module.ts

import { Module } from "@nestjs/common"
import { PassportModule } from "@nestjs/passport"
import { UsersModule } from "src/users/users.module"
import { AuthService } from "./auth.service"
import { LocalStrategy } from "./local.strategy"
import { SessionSerializer } from "./session.serializer"

@Module({
  imports: [UsersModule, PassportModule.register({ session: true })],
  providers: [AuthService, LocalStrategy, SessionSerializer],
})
export class AuthModule {}

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

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

Selamat! 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.

Catatan: 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. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Hubungan Mongo DB antar objek

  2. Manual Audit Database Sumber Terbuka DevOps - Semua Yang Harus Anda Ketahui

  3. Hal-Hal Utama yang Harus Dipantau di MongoDB

  4. Node.js, Mongo menemukan dan mengembalikan data

  5. Saya perlu mengambil objek MongoDB hanya dengan item array yang difilter