احمد نادری

توسعه دهنده فول استک

author
author

احمد نادری

توسعه دهنده فول استک

راه اندازی سرویس احراز هویت در angular

بعد از ایجاد پروژه انگولار گام اول کار ما ایجاد سرویس احراز هویت هست . ما در اینجا از قبلا api هامونو با فریم ورک لاراول درست کردیم و از سیستم otp استفاده میکنیم .

در ابتدا سرویس ها و کامپوننت هایی رو ا دستورهای زیر درست میکنیم :

ng g s _services/auth
ng g s _services/token-storage
ng g s _services/user

ng g c login
ng g c otpverify
ng g c home
ng g c profile
ng g c board-admin
ng g c board-user

به فایل app.module.ts این دو مورد رو اضافه میکنیم :

import { FormsModule } from '@angular/forms';
import { HttpClientModule } from '@angular/common/http';

توجه داشته باشید سرویس ها و کامپوننت هایی که تو مرحله قبل ایجاد کردیم حتما در این فایل اضافه شده باشند .

خب اول میریم سراغ فایل_services/auth.service.ts .این سرویس درخواست‌های ورود به سیستم  بک‌اند ارسال می‌کند.

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { Observable } from 'rxjs';

const AUTH_API = 'http://localhost:8000/api/auth/';

const httpOptions = {
  headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(private http: HttpClient) { }

  login(phone_number: string): Observable<any> {
    return this.http.post(AUTH_API + 'login', {
      phone_number
    }, httpOptions);
  }
  verify(phone_number: stringcode:string): Observable<any> {
    return this.http.post(AUTH_API + 'verifyotp', {
      phone_number,code
    }, httpOptions);
  }

}

و بعد سراغ فایل_services/token-storage.service.ts

TokenStorageService برای مدیریت اطلاعات کاربر در فضای ذخیره‌سازی جلسه مرورگره. برای خروج، ما فقط باید این Session Storage را پاک کنیم.

import { Injectable } from '@angular/core';

const TOKEN_KEY = 'auth-token';
const USER_KEY = 'auth-user';

@Injectable({
  providedIn: 'root'
})
export class TokenStorageService {

  constructor() { }
  signOut(): void {
    window.sessionStorage.clear();
  }

  public saveToken(token: string): void {
    window.sessionStorage.removeItem(TOKEN_KEY);
    window.sessionStorage.setItem(TOKEN_KEY, token);
  }

  public getToken(): string | null {
    return window.sessionStorage.getItem(TOKEN_KEY);
  }

  public saveUser(user: any): void {
    window.sessionStorage.removeItem(USER_KEY);
    window.sessionStorage.setItem(USER_KEY, JSON.stringify(user));
  }

  public getUser(): any {
    const user = window.sessionStorage.getItem(USER_KEY);
    if (user) {
      return JSON.parse(user);
    }

    return {};
  }
}

خب مرحله بعد مربوط به فایل _services/user.service.ts هست .این سرویس روش هایی را برای دسترسی به منابع عمومی و حفاظت شده ارائه می دهد.

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

const API_URL = 'http://localhost:8000/api/test/';

@Injectable({
  providedIn: 'root'
})
export class UserService {
  constructor(private http: HttpClient) { }

  getPublicContent(): Observable<any> {
    return this.http.get(API_URL + 'all', { responseType: 'text' });
  }

  getUserBoard(): Observable<any> {
    return this.http.get(API_URL + 'user', { responseType: 'text' });
  }
  getAdminBoard(): Observable<any> {
    return this.http.get(API_URL + 'admin', { responseType: 'text' });
  }
}
خب تو این قسمت ما از HttpInterceptor استفاده میکنیم . این متد intercept برای بازرسی و تبدیل درخواست های HTTP قبل از ارسال به سروره. برای اینکار فایل _helpers/auth.interceptor.ts رو ایجاد میکنیم :
import { HTTP_INTERCEPTORS, HttpEvent } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';

import { TokenStorageService } from '../_services/token-storage.service';
import { Observable } from 'rxjs';

const TOKEN_HEADER_KEY = 'x-access-token';     
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
  constructor(private token: TokenStorageService) { }

  intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
    let authReq = req;
    const token = this.token.getToken();
    if (token != null) {
      authReq = req.clone({ headers: req.headers.set(TOKEN_HEADER_KEY, 'Bearer ' + token) });
    }
    return next.handle(authReq);
  }
}

export const authInterceptorProviders = [
  { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
];
پیشوند “Bearer” به توکن اضافه می کنیم.

خب در مرحله بعد کامپوننت هارو اصلاح میکنیم :

<div class="col-md-12">
    <div class="card card-container">
     
      <form
        *ngIf="!isLoggedIn"
        name="form"
        (ngSubmit)="f.form.valid && onSubmit()"
        #f="ngForm"
        novalidate
      >
        <div class="form-group">
          <label for="phone_number">phone_number</label>
          <input
            type="text"
            class="form-control"
            name="phone_number"
            [(ngModel)]="form.phone_number"
            required
            #phone_number="ngModel" min="10" max="13"
          />
          <div
            class="alert alert-danger"
            role="alert"
            *ngIf="phone_number.errors && f.submitted"
          >
          phonenumber is required!
          </div>
        </div>
       
        <div class="form-group">
          <button class="btn btn-primary btn-block">
            Login
          </button>
        </div>
        <div class="form-group">
          <div
            class="alert alert-danger"
            role="alert"
            *ngIf="f.submitted && isLoginFailed"
          >
            Login failed: {{ errorMessage }}
          </div>
        </div>
      </form>
  
      <div class="alert alert-success" *ngIf="isLoggedIn">
        Logged in as {{ roles }}.
      </div>
    </div>
  </div>
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../_services/auth.service';
import { TokenStorageService } from '../_services/token-storage.service';
import { HttpClient  } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html',
  styleUrls: ['./login.component.scss']
})
export class LoginComponent implements OnInit {

  form: any = {
    phone_number: null,
  };
  isLoggedIn = false;
  isLoginFailed = false;
  errorMessage = '';
  roles: string[] = [];

  constructor(private authService: AuthService, private tokenStorage: TokenStorageService,private http:HttpClient
    ,private router: Router
    ) { }

  ngOnInit(): void {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getUser().roles;
    }
  }

  onSubmit(): void {
    const { phone_number} = this.form;
    this.authService.login(phone_number,ip,userAgent).subscribe(
      data => {
        this.isLoginFailed = true;
      },
      err => {
        this.errorMessage = err.error.message;
        this.isLoginFailed = true;
      }
    );
  }

}

برای صفحه وارد کردن کد otp ما از پکیج ng-otp-input استفاده میکنیم :‌

<div class="col-md-12">
    <div class="card card-container">
      <form
        *ngIf="!isLoggedIn"
        name="form"
        (ngSubmit)="f.form.valid && onSubmit()"
        #f="ngForm"
        novalidate
      >
      <ng-otp-input #ngOtpInput (onInputChange)="onOtpChange($event)" *ngIf="showOtpComponent" [config]="config" ></ng-otp-input>

       
        <div class="form-group">
          <button class="btn btn-primary btn-block">
            Login
          </button>
        </div>
        <div class="form-group">
          <div
            class="alert alert-danger"
            role="alert"
            *ngIf="f.submitted && isLoginFailed"
          >
            Login failed: {{ errorMessage }}
          </div>
        </div>
      </form>
  
      <div class="alert alert-success" *ngIf="isLoggedIn">
        Logged in as {{ roles }}.
      </div>
    </div>
  </div>
import { Component, OnInit , ViewChild } from '@angular/core';
import { AuthService } from '../_services/auth.service';
import { TokenStorageService } from '../_services/token-storage.service';
import { HttpClient  } from '@angular/common/http';
import { Router } from '@angular/router';

@Component({
  selector: 'app-otpveirfy',
  templateUrl: './otpverify.component.html',
  styleUrls: ['./otpverify.component.scss']
})
export class OtpverifyComponent implements OnInit {

  form: any = {
    phone_number: null,
  };
  isLoggedIn = false;
  isLoginFailed = false;
  errorMessage = '';
  roles: string[] = [];
  ipAddress = '';
  otp: string ='';
  showOtpComponent = true; 

  @ViewChild("ngOtpInput", { static: false }) ngOtpInput: any; config = { allowNumbersOnly: true, length: 5, isPasswordInput: false, disableAutoFocus: false, placeholder: "*", inputStyles: { width: "50px", height: "50px", }, }; 


  constructor(
    private router: Router,
    private authService: AuthService, private tokenStorage: TokenStorageService,private http:HttpClient) { }

  ngOnInit(): void {
    if (this.tokenStorage.getToken()) {
      this.isLoggedIn = true;
      this.roles = this.tokenStorage.getUser().roles;
    }
  }

  onSubmit(): void {
    
    const code:any =  this.form;;    
    this.authService.verify(phone_number,code).subscribe(
      data => {
        console.log('ok');
        this.tokenStorage.saveToken(data.accessToken);
        this.tokenStorage.saveUser(data);
        this.isLoginFailed = false;
        this.isLoggedIn = true;
        this.roles = this.tokenStorage.getUser().roles;
      },
      err => {
        this.errorMessage = err.error.message;
        this.isLoginFailed = true;
      }
    );
  }

  reloadPage(): void {
    window.location.reload();
  }

  onOtpChange($event:any){
    console.log($event);
  }
}

برای کامپوننت پروفایل که کاربر فعلی را از Storage با استفاده از TokenStorageService دریافت می کند و اطلاعات اونو را نشان میده.

import { Component, OnInit } from '@angular/core';
import { TokenStorageService } from '../_services/token-storage.service';

@Component({
  selector: 'app-profile',
  templateUrl: './profile.component.html',
  styleUrls: ['./profile.component.css']
})
export class ProfileComponent implements OnInit {
  currentUser: any;

  constructor(private token: TokenStorageService) { }

  ngOnInit(): void {
    this.currentUser = this.token.getUser();
  }
}

٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫

<div class="container" *ngIf="currentUser; else loggedOut">
  <header class="jumbotron">
    <h3>
      <strong>{{ currentUser.username }}</strong> Profile
    </h3>
  </header>
  <p>
    <strong>Token:</strong>
    {{ currentUser.accessToken.substring(0, 20) }} ...
    {{ currentUser.accessToken.substr(currentUser.accessToken.length - 20) }}
  </p>
  <p>
    <strong>Email:</strong>
    {{ currentUser.email }}
  </p>
  <strong>Roles:</strong>
  <ul>
    <li *ngFor="let role of currentUser.roles">
      {{ role }}
    </li>
  </ul>
</div>

<ng-template #loggedOut>
  Please login.
</ng-template>
و بعد کامپوننت home که از UserService برای دریافت منابع عمومی از back-end استفاده میکنه:
import { Component, OnInit } from '@angular/core';
import { UserService } from '../_services/user.service';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
  styleUrls: ['./home.component.css']
})
export class HomeComponent implements OnInit {
  content?: string;

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getPublicContent().subscribe(
      data => {
        this.content = data;
      },
      err => {
        this.content = JSON.parse(err.error).message;
      }
    );
  }
}
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
<div class="container">
  <header class="jumbotron">
    <p>{{ content }}</p>
  </header>
</div>

بعضی کامپوننت ها مبتنی بر نقش کاربر هستند مثل کامپوننت های مدیریت . اونارو هم باید به شکل زیر تغییر بدیم مثلا:

board-admin/board-admin.component.ts
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
import { Component, OnInit } from '@angular/core';
import { UserService } from '../_services/user.service';

@Component({
  selector: 'app-board-admin',
  templateUrl: './board-admin.component.html',
  styleUrls: ['./board-admin.component.css']
})
export class BoardAdminComponent implements OnInit {
  content?: string;

  constructor(private userService: UserService) { }

  ngOnInit(): void {
    this.userService.getAdminBoard().subscribe(
      data => {
        this.content = data;
      },
      err => {
        this.content = JSON.parse(err.error).message;
      }
    );
  }
}
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫٫
<div class="container">
  <header class="jumbotron">
    <p>{{ content }}</p>
  </header>
</div>

وبعد روت هامونو هم اضافه میکنیم :‌

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { VerifyComponent } from './verify/verify.component';
import { LoginComponent } from './login/login.component';
import { HomeComponent } from './home/home.component';
import { ProfileComponent } from './profile/profile.component';
import { BoardUserComponent } from './board-user/board-user.component';
import { BoardAdminComponent } from './board-admin/board-admin.component';

const routes: Routes = [
  { path: 'home', component: HomeComponent },
  { path: 'login', component: LoginComponent },
  { path: 'verify', component: VerifyComponent },
  { path: 'profile', component: ProfileComponent },
  { path: 'user', component: BoardUserComponent },
  { path: 'admin', component: BoardAdminComponent },
  { path: '', redirectTo: 'home', pathMatch: 'full' }
];

@NgModule({
  imports: [RouterModule.forRoot(routes)],
  exports: [RouterModule]
})
export class AppRoutingModule { }
و در پایان فایل اصلی پروژه رو اینجوری تغییر میدیم :
import { Component, OnInit } from '@angular/core';
import { TokenStorageService } from './_services/token-storage.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {
  private roles: string[] = [];
  isLoggedIn = false;
  showAdminBoard = false;
  username?: string;

  constructor(private tokenStorageService: TokenStorageService) { }

  ngOnInit(): void {
    this.isLoggedIn = !!this.tokenStorageService.getToken();

    if (this.isLoggedIn) {
      const user = this.tokenStorageService.getUser();
      this.roles = user.roles;

      this.showAdminBoard = this.roles.includes('ROLE_ADMIN');

      this.username = user.username;
    }
  }

  logout(): void {
    this.tokenStorageService.signOut();
    window.location.reload();
  }
}
<div id="app">
  <nav class="navbar navbar-expand navbar-dark bg-dark">
    <a href="#" class="navbar-brand">bezKoder</a>
    <ul class="navbar-nav mr-auto" routerLinkActive="active">
      <li class="nav-item">
        <a href="/home" class="nav-link" routerLink="home">Home </a>
      </li>
      <li class="nav-item" *ngIf="showAdminBoard">
        <a href="/admin" class="nav-link" routerLink="admin">Admin Board</a>
      </li>
      <li class="nav-item">
        <a href="/user" class="nav-link" *ngIf="isLoggedIn" routerLink="user">User</a>
      </li>
    </ul>

    <ul class="navbar-nav ml-auto" *ngIf="!isLoggedIn">
      <li class="nav-item">
        <a href="/register" class="nav-link" routerLink="register">Sign Up</a>
      </li>
      <li class="nav-item">
        <a href="/login" class="nav-link" routerLink="login">Login</a>
      </li>
    </ul>

    <ul class="navbar-nav ml-auto" *ngIf="isLoggedIn">
      <li class="nav-item">
        <a href="/profile" class="nav-link" routerLink="profile">{{ username }}</a>
      </li>
      <li class="nav-item">
        <a href class="nav-link" (click)="logout()">LogOut</a>
      </li>
    </ul>
  </nav>

  <div class="container">
    <router-outlet></router-outlet>
  </div>

درباره من
  • سلام به وب سایت من خوش اومدید ! از سال 1388 که وارد دنیای برنامه نویسی شدم تجربیات خودم رو از پروژه های مختلف به دست آوردم و همیشه سعی کردم تا مهارت های فنی و رفتاری خودم رو ارتقا بدم تا بتونم برای خودم و سازمانی که در آن کار می کنم بهترین باشم . من عاشق یادگیری هستم و همیشه از چالش های جدید هیجان زده میشم چون اونارو کلید موفقیت و رشد خودم می دونم.
  • @ahmadnaderi01
arrow