طريقة تسجيل الدخول للمرة الأخيرة | Login

الواجهة الخلفية


 التثبيتات في الواجهة الخلفية:

npm install bcryptjs

npm install passport-jwt

npm i @nestjs/passport@10.0.2*

npm @types/bcrypt@5.0.0

npm install jsonwebtoken


https://g.co/gemini/share/0b4dc960b41f


الخطوة الأولى: إنشاء ملف login.dto.ts داخل مجلد dto

import { IsEmail, IsNotEmpty } from 'class-validator';

export class LoginDto {
  @IsEmail()
  email: string;

  @IsNotEmpty()
  password: string;
}


الخطوة الثانية: إنشاء مجلد refresh-token.dto.ts داخل مجلد dto أيضاً

import { IsNotEmpty } from 'class-validator';

class RefreshTokenDto {
  @IsNotEmpty()
  refreshToken: string;
}

export default RefreshTokenDto;

الخطوة الثالثة: إنشاء ملف refresh-token.entity.ts في مجلد entities

import { sign } from 'jsonwebtoken';

class RefreshToken {
  constructor(init?: Partial<RefreshToken>) {
    Object.assign(this, init);
  }

  id: number;
  userId: number;
  userAgent: string;
  ipAddress: string;

  sign(): string {
    return sign({ ...this }, 'DAA5761515184B7221F531515533C1F23F31FD391515149');
  }
}

export default RefreshToken;

الخطوة الرابعة: إنشاء مجلد باسم guards  وإنشاء ملف jwt-auth.guard.ts  بداخله:

import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthGuard } from '@nestjs/passport';
import { JsonWebTokenError } from 'jsonwebtoken';

@Injectable()
export class JwtAuthGuard extends AuthGuard('jwt') {
  handleRequest(err: any, user: any, info: any, context: any, status: any) {
    if (info instanceof JsonWebTokenError) {
      throw new UnauthorizedException('Invalid JWT');
    }

    return super.handleRequest(err, user, info, context, status);
  }
}

الخطوة الخامسة: إنشاء مجلد باسم strategies وإنشاء ملف jwt.strategy.ts  بداخله:

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

export class JwtStrategy extends PassportStrategy(Strategy) {
  constructor() {
    super({
      jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
      ignoreExpiration: false,
      secretOrKey: '7A12548484AS4455D67315515E2D5E29',
    });
  }

  validate(payload) {
    return {
      userId: payload.userId,
    };
  }
}

الخطوة السادسة: تعديل ملف الـ Module ليكون بهذا النحو:

import { Module } from '@nestjs/common';
import { EmployeeCardService } from './employee-card.service';
import { EmployeeCardController } from './employee-card.controller';
import { TypeOrmModule } from '@nestjs/typeorm';
import { EmployeeCard } from './entities/employee-card.entity';
import { JwtStrategy } from './strategies/jwt.strategy';

@Module({
  imports: [
    TypeOrmModule.forFeature([EmployeeCard])],
  controllers: [EmployeeCardController],
  providers: [EmployeeCardService, JwtStrategy],
  exports: [EmployeeCardService, EmployeeCardService]
})
export class EmployeeCardModule { }

الخطوة السابعة: إضافة الدوال التالية إلى الخدمة Service.ts

const bcrypt = require('bcryptjs');

  private refreshTokens: RefreshToken[] = [];


  private async newRefreshAndAccessToken(
    user: EmployeeCard,
    values: { userAgent: string; ipAddress: string },
  ): Promise<{ accessToken: string; refreshToken: string }> {
    const refreshObject = new RefreshToken({
      id:
        this.refreshTokens.length === 0
          ? 0
          : this.refreshTokens[this.refreshTokens.length - 1].id + 1,
      ...values,
      userId: user.id,
    });
    this.refreshTokens.push(refreshObject);

    return {
      refreshToken: refreshObject.sign(),
      accessToken: sign(
        {
          userId: user.id,
        },
        '7A12548484AS4455D67315515E2D5E29',
        {
          expiresIn: '24h',
        },
      ),
    };
  }

  async signIn(user: EmployeeCard,
    values: { userAgent: string; ipAddress: string },
  ): Promise<{ accessToken: string; refreshToken: string } | undefined> {
    const userDB = await this.employeeCardRepository.findOne({
      where: {
        email: user.email.toString(),
      }
    });

    if (!userDB) {
      return {accessToken: null, refreshToken: null};
    }

    // verify your user -- use argon2 for password hashing!!
    if (!bcrypt.compareSync(user.password, userDB.password)) {
      return undefined;
    }
    return this.newRefreshAndAccessToken(user, values);
  }

async _findOneByEmail(email: string): Promise<any> {
    return await this.employeeCardRepository.findOne({
      where: {
        email: email,
      },
      relations: ['branch']
      , select: {
        // قم بعدم تحديد كلمة المرور
        id: true,
        type_of_card: true,
        employee_id: true,
        ar_employee_name: true,
        en_employee_name: true,
        national_id: true,
        blood_type: true,
        front_card: true,
        buffer: true,
        language: true,
        template: true,
        mobile: true,
        email: true,
        hiring_date: true,
        expireDate: true,
        completed_tasks: true,
        monthly_target: true,
        query_user: true,
        document_type: true,
        education_level: true,
        status: true,
        gender: true,
        marital_status: true,
        nationality: true,
        phone_no: true,
        fax_no: true,
        telegram_chat_id: true,
        address: true,
        insurance_pin: true,
        basic_salary: true,
        salary_allowances: true,
        bank_name: true,
        bank_branch: true,
        bank_account_number: true,
        bank_account_iban: true,
        birth_date: true,
        query_password: true,
        technician: true,
        created_at: true,
        modified_at: true,
        created_by: true,
        modified_by: true
      }
    });
  }

  async patchPassword(user: EmployeeCard) {
    bcrypt.hash(user.password, 10)  // Salt rounds = 10
      .then(hashedPassword => {
        user.password = hashedPassword;
        return this.employeeCardRepository.save(user);
      });
  }


الخطوة الثامنة: قم بإضافة الدوال التالية إلى المراقب Controller.ts

  //------------------- jwt -----------------------
  @UseGuards(JwtAuthGuard)
  @Get('me')
  me(@Headers() headers: Headers) {
    // `allHeaders` will contain all the request headers
    const allHeaders:any = headers;
    return this.employeeCardsService._findOneByEmail(allHeaders.email);
  }

  @Post('login')
  signIn(@Req() request?, @Ip() ip?: string, @Body() item?: any) {
    return this.employeeCardsService.signIn(item, {
      ipAddress: ip,
      userAgent: request.headers['user-agent'],
    });
  }
  @Patch('change-password')
  changePassword(@Body() employeeCard: EmployeeCard): Promise<any> {
    return this.employeeCardsService.patchPassword(employeeCard);
  }


الواجهة الأمامية


الخطوة الأولى: قم بإنشاء مجلد اسم helpers وقم بإنشاء المجلدات التالية فيه:

1- auth.guard.ts

2- auth.interceptor.ts

3- basic-auth.interceptor.ts

4- error.interceptor.ts

5- index.ts

6- remove-headers-interceptor.ts

محتويات الملفات كالتالي:

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';


@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    constructor(private router: Router) { }
    canActivate(
        next: ActivatedRouteSnapshot,
        state: RouterStateSnapshot): boolean {
        if (localStorage.getItem('user') != null)
            return true;
        this.router.navigate(['/login']);
        return false;
    }
}


import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpErrorResponse, HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class AuthInterceptor implements HttpInterceptor {
    constructor() { }
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<unknown>> {
        // Get the access token from local storage or any other source
        const accessToken: any = localStorage.getItem('user') || '{}';
        let a = JSON.parse(accessToken);

        // Clone the request and add the authorization header
        const authRequest = request.clone({
            setHeaders: {
                'Content-Type': 'application/json',
                'Accept': 'application/json',
                'Authorization': `bearer ${a.accessToken}`,
            },
        });

        // Pass the cloned request with the authorization header to the next handler
        return next.handle(authRequest);
    }
}


import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class BasicAuthInterceptor implements HttpInterceptor {
  constructor() { }

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    const accessToken: any = localStorage.getItem('user') || '{}';
    let a = JSON.parse(accessToken);
    // Get the username and password for basic authentication
    // Add the Authorization header with the base64 encoded credentials
    let authRequest = request.clone({
      setHeaders: {
        'Content-Type': 'application/json',
        'Accept': 'application/json',
        'Authorization': `bearer ${a.accessToken}`,
      },
    });

    // Pass the modified request to the next interceptor or to the HTTP handler
    return next.handle(authRequest);
  }

  clearIntercept(request: HttpRequest<any>, next: HttpHandler) {
    const authRequest = request.clone({
      setHeaders: {
        'Content-Type': '',
        'Accept': '',
        'Authorization': ``,
      },
    });

    // Pass the modified request to the next interceptor or to the HTTP handler
    return next.handle(authRequest);
  }
}


import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent, HttpResponse, HttpErrorResponse } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';
import { EmployeeCardsService } from '../services/employee-cards.service';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
  constructor(private employeesService:EmployeeCardsService) {}

  intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    return next.handle(request).pipe(
      catchError((error: HttpErrorResponse) => {
        let errorMessage = '';

        if (error.error instanceof ErrorEvent) {
          // Client-side error
          this.employeesService?.logout();
          errorMessage = `Error: ${error.error.message}`;
        } else {
          // Server-side error
          errorMessage = `Error Code: ${error.status}\nMessage: ${error.message}`;
          this.employeesService?.logout();
        }

        console.error(errorMessage);
        return throwError(errorMessage);
      })
    );
  }
}


export * from './auth.guard';
export * from './basic-auth.interceptor';
export * from './error.interceptor';


import { Injectable } from '@angular/core';
import { HttpEvent, HttpHandler, HttpRequest, HttpHeaders, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';

@Injectable()
export class RemoveHeadersInterceptor implements HttpInterceptor {
    intercept(req: HttpRequest<any>, next: HttpHandler):
        Observable<HttpEvent<any>> {
        const newReq = req.clone({ headers: new HttpHeaders() });
        return next.handle(newReq);
    }
}


الخطوة الثانية: تعديل دوال الخدمة للتوافق مع الواجهة الخلفية

constructor(
    private http: HttpClient,
    private router: Router,
    private localStorageService: LocalStorageService,
    private snackBarService: SnackBarService,
    private activityService: ActivitiesService,
    private handleTranslateService: HandleTranslateService,
    private translateService: TranslateService
  ) {
    this.userSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('user') || '{}'));
    this.user = this.userSubject.asObservable();
  }

  private userSubject: BehaviorSubject<any>;
  public user: Observable<any>;
  bearerToken = JSON.parse(localStorage.getItem('user') || '{}');

  public get userValue(): any {
    if (this.userSubject.value != '{}') {
      return this.userSubject.value;
    } else {
      this.router.navigate(['/login']);
      return null;
    }
  }

  //login
  public login(item: any): any {
    this.http.post(`${this.backEndURL}/${this.table}/login`, item, { withCredentials: false }).subscribe((res: any) => {
      if (res.accessToken) {
        this.localStorageService.setUser(res);
        this.getUserInfo(item).subscribe(res => {
          this.localStorageService.setCurrentUserInfo(res);
          this.userSubject.next(res);
          this.router.navigate(['/']);
          this.snackBarService.success(this.translateService.instant('SUCCESS.LOGIN'), '');
        });
      } else {
        this.snackBarService.danger(this.translateService.instant('ERRORS.USER_NOT_FOUND'), '');
      }
    });
  }

  //---------------------------- Logout ------------------------------------
  logout() {
    this.localStorageService.removeUserItem();
    this.userSubject.next(null);
    this.router.navigate(['/login']);
  }

  getUserInfo(user: any) {
    // Get the access token from local storage or any other source

    // Replace 'YOUR_BEARER_TOKEN' with the actual bearer token
    let accessToken = this.bearerToken.accessToken;

    // Create the headers object
    const headers = new HttpHeaders().set('Content-Type', 'application/json');
    const options = { headers: headers.set('Authorization', `bearer ${accessToken}`).set('email', user.email), };
    return this.http.get(`${this.backEndURL}/${this.table}/me`, options);
  }

الخطوة الثالثة: إقفال الصفحات التي تحتاج أذن لتسجل الدخول من خلال ملف app.routing.ts

import { NgModule } from '@angular/core';
import { Routes, RouterModule, PreloadAllModules } from '@angular/router';
import { PagesComponent } from './pages/pages.component';
import { BlankComponent } from './pages/blank/blank.component';
import { SearchComponent } from './pages/search/search.component';
import { NotFoundComponent } from './pages/errors/not-found/not-found.component';
import { ErrorComponent } from './pages/errors/error/error.component';
import { AuthGuard } from './helpers';

export const routes: Routes = [
    {
        path: '', canActivate: [AuthGuard],
        component: PagesComponent, children: [
            { path: '', loadChildren: () => import('./pages/dashboard/dashboard.module').then(m => m.DashboardModule), data: { breadcrumb: 'لوحة التحكم' } },
            { path: 'users', loadChildren: () => import('./pages/users/users.module').then(m => m.UsersModule), data: { breadcrumb: 'Users' } },
            { path: 'dynamic-menu', loadChildren: () => import('./pages/dynamic-menu/dynamic-menu.module').then(m => m.DynamicMenuModule), data: { breadcrumb: 'Dynamic Menu' } },
            { path: 'ui', loadChildren: () => import('./pages/ui/ui.module').then(m => m.UiModule), data: { breadcrumb: 'UI' } },
            { path: 'mailbox', loadChildren: () => import('./pages/mailbox/mailbox.module').then(m => m.MailboxModule), data: { breadcrumb: 'Mailbox' } },
            { path: 'chat', loadChildren: () => import('./pages/chat/chat.module').then(m => m.ChatModule), data: { breadcrumb: 'Chat' } },
            { path: 'form-controls', loadChildren: () => import('./pages/form-controls/form-controls.module').then(m => m.FormControlsModule), data: { breadcrumb: 'Form Controls' } },
            { path: 'tables', loadChildren: () => import('./pages/tables/tables.module').then(m => m.TablesModule), data: { breadcrumb: 'Tables' } },
            { path: 'schedule', loadChildren: () => import('./pages/schedule/schedule.module').then(m => m.ScheduleModule), data: { breadcrumb: 'Schedule' } },
            { path: 'maps', loadChildren: () => import('./pages/maps/maps.module').then(m => m.MapsModule), data: { breadcrumb: 'Maps' } },
            { path: 'charts', loadChildren: () => import('./pages/charts/charts.module').then(m => m.ChartsModule), data: { breadcrumb: 'Charts' } },
            { path: 'drag-drop', loadChildren: () => import('./pages/drag-drop/drag-drop.module').then(m => m.DragDropModule), data: { breadcrumb: 'Drag & Drop' } },
            { path: 'icons', loadChildren: () => import('./pages/icons/icons.module').then(m => m.IconsModule), data: { breadcrumb: 'Material Icons' } },
            { path: 'profile', loadChildren: () => import('./pages/profile/profile.module').then(m => m.ProfileModule), data: { breadcrumb: 'Profile' } },
            { path: 'blank', component: BlankComponent, data: { breadcrumb: 'Blank page' } },
            { path: 'search', component: SearchComponent, data: { breadcrumb: 'Search' } },
            { path: 'search/:name', component: SearchComponent, data: { breadcrumb: 'Search' } }
        ]
    },
    { path: 'landing', loadChildren: () => import('./pages/landing/landing.module').then(m => m.LandingModule) },
    { path: 'login', loadChildren: () => import('./pages/login/login.module').then(m => m.LoginModule) },
    { path: 'register', loadChildren: () => import('./pages/register/register.module').then(m => m.RegisterModule) },
    { path: 'error', component: ErrorComponent, data: { breadcrumb: 'Error' } },
    { path: '**', component: NotFoundComponent }
];

@NgModule({
    imports: [
        RouterModule.forRoot(routes, {
            preloadingStrategy: PreloadAllModules
        })
    ],
    exports: [
        RouterModule
    ]
})
export class AppRoutingModule { }

الخطوة الرابعة: طريقة الاستدعاء في صفحة الـ Login

constructor(
    private http: HttpClient,
    private router: Router,
    private localStorageService: LocalStorageService,
    private snackBarService: SnackBarService,
    private activityService: ActivitiesService,
    private handleTranslateService: HandleTranslateService,
    private translateService: TranslateService
  ) {
    this.userSubject = new BehaviorSubject<any>(JSON.parse(localStorage.getItem('user') || '{}'));
    this.user = this.userSubject.asObservable();

    this.backEndURL = this.getBackEndUrl();

    if (this.translateService.currentLang === 'ar') {
      this.handleTranslateService.getArLangFile().subscribe(res => {
        this.handleLang = res;
      })
    } else {
      this.handleTranslateService.getEnLangFile().subscribe(res => {
        this.handleLang = res;
      })
    }
    this.successMsg = this.handleLang['SUCCESS'];
  }

  private userSubject: BehaviorSubject<any>;
  public user: Observable<any>;
  bearerToken = JSON.parse(localStorage.getItem('user') || '{}');

  public get userValue(): any {
    if (this.userSubject.value != '{}') {
      return this.userSubject.value;
    } else {
      this.router.navigate(['/login']);
      return null;
    }
  }

  //login
  public login(item: any): any {
    this.http.post(`${this.backEndURL}/${this.table}/login`, item, { withCredentials: false }).subscribe((res: any) => {
      if (res.accessToken) {
        this.localStorageService.setUser(res);
        this.bearerToken = JSON.parse(localStorage.getItem('user') || '{}');
        setTimeout(() => {
          this.getUserInfo(item).subscribe(res => {
            this.localStorageService.setCurrentUserInfo(res);
            this.userSubject.next(res);
            this.router.navigate(['/']);
            this.snackBarService.success(this.translateService.instant('SUCCESS.LOGIN'), '');
          });
        }, 500);
      } else {
        this.snackBarService.danger(this.translateService.instant('ERRORS.USER_NOT_FOUND'), '');
      }
    });
  }

  //---------------------------- Logout ------------------------------------
  logout() {
    this.localStorageService.removeUserItem();
    this.userSubject.next(null);
    this.router.navigate(['/login']);
  }

  getUserInfo(user: any) {
    // Get the access token from local storage or any other source

    // Replace 'YOUR_BEARER_TOKEN' with the actual bearer token
    let accessToken = this.bearerToken.accessToken;

    // Create the headers object
    const headers = new HttpHeaders().set('Content-Type', 'application/json');
    const options = { headers: headers.set('Authorization', `bearer ${accessToken}`).set('email', user.email), };
    return this.http.get(`${this.backEndURL}/${this.table}/me`, options);
  }

الخطوة الأخيرة تسجيل الخروج

import { Component, OnInit, ViewEncapsulation } from '@angular/core';
import { EmployeeCardsService } from 'src/app/services/employee-cards.service';

@Component({
  selector: 'app-user-menu',
  templateUrl: './user-menu.component.html',
  styleUrls: ['./user-menu.component.scss'],
  encapsulation: ViewEncapsulation.None,
})
export class UserMenuComponent implements OnInit {
  public userImage = "assets/img/users/user.jpg";
  constructor(private employeeCardsService: EmployeeCardsService) { }

  ngOnInit() {
  }

  logOut(){
    this.employeeCardsService.logout();
  }
}


<button mat-icon-button [matMenuTriggerFor]="userMenu" #userMenuTrigger="matMenuTrigger" >
    <mat-icon>account_circle</mat-icon>
</button>

<mat-menu #userMenu="matMenu" [overlapTrigger]="false" class="toolbar-dropdown-menu user-menu">
    <!--<span (mouseleave)="userMenuTrigger.closeMenu()">-->        
        <mat-card fxLayout="row" fxLayoutAlign="space-around center" class="user-info p-0">
            <img [src]="userImage" alt="user-image">
            <div fxLayout="column" fxLayoutAlign="center center">
                <h2>Emilio Verdines</h2>
                <h3 class="secondary-text-color">Web developer</h3>
                <p class="secondary-text-color">Member since May. 2016</p>
            </div>
        </mat-card>  
        <mat-divider></mat-divider>
        <a mat-menu-item routerLink="/profile/projects">
            <mat-icon>person</mat-icon>
            <span>Profile</span>
        </a>
        <a mat-menu-item routerLink="/profile/user-info">
            <mat-icon>edit</mat-icon>
            <span>Edit Profile</span>
        </a>
        <a mat-menu-item routerLink="settings">
            <mat-icon>settings</mat-icon>
            <span>Settings</span>
        </a>
        <a mat-menu-item routerLink="/">
            <mat-icon>lock</mat-icon>
            <span>Lock screen</span>
        </a>
        <a mat-menu-item routerLink="help">
            <mat-icon>help</mat-icon>
            <span>Help</span>
        </a>
        <mat-divider></mat-divider>
        <a (click)="logOut()" mat-menu-item routerLink="/login">
            <mat-icon>power_settings_new</mat-icon>
            <span>Log out</span>
        </a>
    <!--</span>-->
</mat-menu>


تعليقات

المشاركات الشائعة من هذه المدونة

ngx-extended-pdf-viewer

how to getting access token for https://fcm.googleapis.com/v1/projects/al-ayahya-co/messages:send for angular and backend nestjs

طريقة تفعيل زر Inline Keyboard في Telegram Bot