احراز هویت در NestJS

آموزش NestJS | معراج پارسا
آموزش NestJS | معراج پارسا


سلام دوستان

امروز می‌خوایم به صورت مفصل راجع به احراز هویت توی NestJS صحبت کنیم . امنیت تو برنامه‌های وب یکی از مهم‌ترین مسائله و احراز هویت نقش کلیدی توی این زمینه داره . تو این پست یاد می‌گیریم چطور با استفاده از NestJS یه سیستم احراز هویت قوی و کارآمد بسازیم . پس آماده‌اید؟ بریم سراغش



اگه نمیدونی NestJS چیه :
NestJS یکی از فریم‌ورک‌های جذاب برای توسعه‌ی برنامه‌های سمت سروره ، دوس داری بیشتر باهاش آشنا بشی ؟ این پست رو ببین و اگه خواستی سریع یادش بگیری و اولین برنامه خودتو ایجاد کنی قسمت اول و قسمت دوم رو ببین


مفاهیم پایه‌ای احراز هویت

قبل از اینکه بریم سراغ کد، لازمه یه سری مفاهیم پایه‌ای رو مرور کنیم. احراز هویت (Authentication) فرآیند تأیید هویت کاربره. بعد از تأیید هویت، می‌تونیم از مجوزدهی (Authorization) برای تعیین سطح دسترسی کاربر استفاده کنیم.


احراز هویت مبتنی بر Token

یکی از رایج‌ترین روش‌های احراز هویت تو برنامه‌های وب، استفاده از توکن‌هاست. JWT (JSON Web Token) یکی از محبوب‌ترین استانداردها برای این کاره. توکن JWT شامل اطلاعاتی مثل شناسه کاربر و تاریخ انقضاست و بعد از ورود موفقیت‌آمیز کاربر، بهش داده می‌شه تا تو درخواست‌های بعدی ازش استفاده کنه .


نصب و راه‌اندازی کتابخانه‌های مورد نیاز

برای پیاده‌سازی احراز هویت با JWT توی NestJS، باید چند تا کتابخانه نصب کنیم :

npm install --save @nestjs/jwt @nestjs/passport passport passport-jwt
 bcryptjs

کتابخانه‌های @nestjs/jwt و @nestjs/passport برای کار با JWT و Passport استفاده می‌شن ، کتابخانه passport یه فریم‌ورک احراز هویته و passport-jwt یه استراتژی برای استفاده از JWT با Passport فراهم می‌کنه . کتابخانه bcryptjs هم برای هش کردن رمز عبور استفاده می‌شه .


پیکربندی JWT

اول از همه، باید ماژول JWT رو پیکربندی کنیم. این کار رو تو فایل auth.module.ts انجام می‌دیم :

import { Module } from '@nestjs/common';
import { JwtModule } from '@nestjs/jwt';
import { PassportModule } from '@nestjs/passport';
import { AuthService } from './auth.service';
import { AuthController } from './auth.controller';
import { UsersModule } from '../users/users.module';
import { JwtStrategy } from './jwt.strategy';

@Module(
     { 
         imports: [
                UsersModule,
                PassportModule,
               JwtModule.register({ 
                         secret: 'secretKey', // کلید مخفی برای امضای توکن‌ها 
                         signOptions: { expiresIn: '60m' }, // مدت زمان اعتبار توکن‌ها
                          }),  
         ], providers: [AuthService, JwtStrategy],   controllers: [AuthController], }
) 
export class AuthModule {}

اینجا ماژول JWT رو با یه کلید مخفی و مدت زمان اعتبار توکن پیکربندی کردیم. همچنین، ماژول Passport و استراتژی JWT رو هم اضافه کردیم .


ایجاد سرویس احراز هویت

حالا بریم سراغ ایجاد سرویس احراز هویت. این سرویس مسئولیت‌های اصلی مثل ورود کاربر، تولید توکن JWT و تأیید هویت کاربر رو بر عهده داره. فایل auth.service.ts به این شکل خواهد بود :

import { Injectable } from '@nestjs/common';
import { JwtService } from '@nestjs/jwt';
import { UsersService } from '../users/users.service';
import * as bcrypt from 'bcryptjs';

@Injectable()
export class AuthService {
            constructor(
                 private readonly usersService: UsersService ,
                 private readonly jwtService: JwtService,
            ) {}

            async validateUser(username: string, pass: string): Promise<any> {
                  const user = await this.usersService.findOne(username);
                  if (user && bcrypt.compareSync(pass, user.password)) {
                           const { password, ...result } = user; 
                           return result; 
                  }
                  return null;
             }

             async login(user: any) {
                   const payload = { username: user.username, sub: user.userId };
                   return { access_token: this.jwtService.sign(payload),     };
             }
 }

تو این سرویس، متد validateUser برای تأیید هویت کاربر استفاده می‌شه و متد login یه توکن JWT برای کاربر تولید می‌کنه .


ایجاد کنترلر احراز هویت

کنترلر احراز هویت وظیفه مدیریت درخواست‌های مربوط به احراز هویت رو داره. فایل auth.controller.ts به این شکل خواهد بود :

import { Controller, Request, Post, UseGuards } from '@nestjs/common'; 
import { AuthService } from './auth.service'; 
import { LocalAuthGuard } from './local-auth.guard'; 

@Controller('auth') 
export class AuthController { 
  constructor(private readonly authService: AuthService) {} 
  @UseGuards(LocalAuthGuard) 
  @Post('login') 
  async login(@Request() req) { 
    return this.authService.login(req.user); 
  } 
}

این کنترلر یه مسیر /auth/login ایجاد می‌کنه که از گارد محلی LocalAuthGuard برای تأیید هویت استفاده می‌کنه .


ایجاد گاردهای احراز هویت

برای مدیریت احراز هویت، نیاز به گاردهای مختلفی داریم. اولین گارد، LocalAuthGuard، مسئول تأیید هویت کاربر با استفاده از نام کاربری و رمز عبوره. فایل local-auth.guard.ts به این شکل خواهد بود :

import { Injectable } from '@nestjs/common'; 
import { AuthGuard } from '@nestjs/passport'; 

@Injectable() 
export class LocalAuthGuard extends AuthGuard('local') {}

برای ایجاد استراتژی محلی، فایل local.strategy.ts به این شکل خواهد بود :

import { Strategy } from 'passport-local'; 
import { PassportStrategy } from '@nestjs/passport'; 
import { Injectable, UnauthorizedException } from '@nestjs/common'; 
import { AuthService } from './auth.service';  

@Injectable() 
export class LocalStrategy extends PassportStrategy(Strategy) { 
  constructor(private authService: AuthService) { 
    super(); 
  } 

  async validate(username: string, password: string): Promise<any> { 
    const user = await this.authService.validateUser(username, password); 
    if (!user) { 
      throw new UnauthorizedException(); 
    } 
    return user; 
  } 
}

گارد دوم، JwtAuthGuard، مسئول تأیید توکن JWT برای دسترسی به مسیرهای محافظت‌شده‌ست. فایل jwt-auth.guard.ts به این شکل خواهد بود :

import { Injectable } from '@nestjs/common'; 
import { AuthGuard } from '@nestjs/passport'; 

@Injectable() 
export class JwtAuthGuard extends AuthGuard('jwt') {}

برای ایجاد استراتژی JWT، فایل jwt.strategy.ts به این شکل خواهد بود :

import { Injectable } from '@nestjs/common'; 
import { PassportStrategy } from '@nestjs/passport'; 
import { ExtractJwt, Strategy } from 'passport-jwt'; 

@Injectable() 
export class JwtStrategy extends PassportStrategy(Strategy) { 
  constructor() { 
    super({ 
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), 
      ignoreExpiration: false, 
      secretOrKey: 'secretKey', // کلید مخفی برای اعتبارسنجی توکن‌ها 
    });
  }

  async validate(payload: any) {
    return { userId: payload.sub, username: payload.username };
  }
}

ایجاد کاربران

حالا باید بخشی برای مدیریت کاربران داشته باشیم. تو فایل users.service.ts، می‌تونیم متدهایی برای پیدا کردن کاربر بر اساس نام کاربری و ایجاد کاربر جدید داشته باشیم :

import { Injectable } from '@nestjs/common';
import { User } from './user.entity';
import { Repository } from 'typeorm';
import { InjectRepository } from '@nestjs/typeorm';
import * as bcrypt from 'bcryptjs';

@Injectable()
export class UsersService {
  constructor(
    @InjectRepository(User)
    private usersRepository: Repository<User>,
  ) {}

  async findOne(username: string): Promise<User | undefined> {
    return this.usersRepository.findOne({ where: { username } });
  }

  async create(username: string, password: string): Promise<User> {
    const hashedPassword = await bcrypt.hash(password, 10);
    const newUser = this.usersRepository.create({ username, password: hashedPassword });
    return this.usersRepository.save(newUser);
  }
}

فایل user.entity.ts هم به این شکل خواهد بود :

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  password: string;
}

محافظت از مسیرهای مختلف

برای محافظت از مسیرهای مختلف، می‌تونید از JwtAuthGuard استفاده کنید. به عنوان مثال، تو یه کنترلر دیگه می‌تونید این کار رو انجام بدید :

import { Controller, Get, UseGuards } from '@nestjs/common';
import { JwtAuthGuard } from './auth/jwt-auth.guard';

@Controller('profile')
export class ProfileController {
  @UseGuards(JwtAuthGuard)
  @Get()
  getProfile(@Request() req) {
    return req.user;
  }
}


جمع بندی

خب دوستان ، تو این پست به صورت مفصل با احراز هویت توی NestJS آشنا شدیم و یاد گرفتیم چطور می‌تونیم یه سیستم احراز هویت قوی با استفاده از JWT و Passport بسازیم . حالا می‌تونید از این ترفندها و تکنیک‌ها تو پروژه‌های خودتون استفاده کنید و امنیت برنامه‌هاتون رو افزایش بدید . اگه سوال یا نظری داشتید، خوشحال می‌شم که با من در میون بذارید ، یاعلی


مخلصیم:)