- Angular 教程
- Angular - 首頁
- Angular - 概述
- Angular - 功能
- Angular - 優點與缺點
- Angular 基礎
- Angular - 環境設定
- Angular - 第一個應用程式
- Angular - MVC 架構
- Angular 元件
- Angular - 元件
- Angular - 元件生命週期
- Angular - 檢視封裝
- Angular - 元件互動
- Angular - 元件樣式
- Angular - 巢狀元件
- Angular - 內容投影
- Angular - 動態元件
- Angular - 元素
- Angular 模板
- Angular - 模板
- Angular - 文字插值
- Angular - 模板語句
- Angular - 模板中的變數
- Angular - SVG 作為模板
- Angular 繫結
- Angular - 繫結及其型別
- Angular - 資料繫結
- Angular - 事件繫結
- Angular - 屬性繫結
- Angular - 屬性繫結
- Angular - 類和樣式繫結
- Angular 指令
- Angular - 指令
- Angular - 內建指令
- Angular 管道
- Angular - 管道
- Angular - 使用管道轉換資料
- Angular 依賴注入
- Angular - 依賴注入
- Angular HTTP 客戶端程式設計
- Angular - 服務
- Angular - HTTP 客戶端
- Angular - 請求
- Angular - 響應
- Angular - 獲取
- Angular - PUT
- Angular - DELETE
- Angular - JSON-P
- Angular - 使用 HTTP 進行 CRUD 操作
- Angular 路由
- Angular - 路由
- Angular - 導航
- Angular - Angular Material
- Angular 動畫
- Angular - 動畫
- Angular 表單
- Angular - 表單
- Angular - 表單驗證
- Angular Service Workers 和 PWA
- Angular - Service Workers 和 PWA
- Angular 測試
- Angular - 測試概述
- Angular NgModules
- Angular - 模組介紹
- Angular 高階
- Angular - 身份驗證與授權
- Angular - 國際化
- Angular - 可訪問性
- Angular - Web Workers
- Angular - 伺服器端渲染
- Angular - Ivy 編譯器
- Angular - 使用 Bazel 構建
- Angular - 向後相容性
- Angular - 響應式程式設計
- Angular - 在指令和元件之間共享資料
- Angular 工具
- Angular - CLI
- Angular 雜項
- Angular - 第三方控制元件
- Angular - 配置
- Angular - 顯示資料
- Angular - 裝飾器和元資料
- Angular - 基本示例
- Angular - 錯誤處理
- Angular - 測試和構建專案
- Angular - 生命週期鉤子
- Angular - 使用者輸入
- Angular - 新功能?
- Angular 有用資源
- Angular - 快速指南
- Angular - 有用資源
- Angular - 討論
Angular - 身份驗證與授權
身份驗證是將 Web 應用程式的訪問者與系統中預定義的使用者身份集進行匹配的過程。換句話說,它是識別使用者身份的過程。在系統安全方面,身份驗證是一個非常重要的過程。
授權是授予使用者訪問系統中某些資源的許可權的過程。只有經過身份驗證的使用者才能被授權訪問資源。
在本節中,讓我們學習如何在 Angular 應用程式中進行身份驗證和授權。
路由中的守衛
在 Web 應用程式中,資源由 URL 引用。系統中的每個使用者都將被允許訪問一組 URL。例如,管理員可能會被分配到管理部分下的所有 URL。
正如我們已經知道的,URL 由路由處理。Angular 路由使 URL 能夠根據程式設計邏輯進行保護和限制。因此,URL 可能會被拒絕普通使用者訪問,而允許管理員訪問。
Angular 提供了一個名為路由守衛的概念,可用於透過路由阻止對應用程式某些部分的未授權訪問。Angular 提供多個守衛,如下所示
CanActivate:用於阻止訪問路由。
CanActivateChild:用於阻止訪問子路由。
CanDeactivate:用於停止正在進行的過程並從使用者那裡獲取反饋。例如,如果使用者回覆否,則可以停止刪除過程。
Resolve:用於在導航到路由之前預取資料。
CanLoad:用於載入資產。
工作示例
讓我們嘗試向我們的應用程式新增登入功能並使用 CanActivate 守衛對其進行保護。
開啟命令提示符並轉到專案根資料夾。
cd /go/to/expense-manager
啟動應用程式。
ng serve
建立一個新的服務 AuthService 來驗證使用者。
ng generate service auth CREATE src/app/auth.service.spec.ts (323 bytes) CREATE src/app/auth.service.ts (133 bytes)
開啟AuthService幷包含以下程式碼。
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { tap, delay } from 'rxjs/operators';
@Injectable({
providedIn: 'root'
})
export class AuthService {
isUserLoggedIn: boolean = false;
login(userName: string, password: string): Observable {
console.log(userName);
console.log(password);
this.isUserLoggedIn = userName == 'admin' && password == 'admin';
localStorage.setItem('isUserLoggedIn', this.isUserLoggedIn ? "true" : "false");
return of(this.isUserLoggedIn).pipe(
delay(1000),
tap(val => {
console.log("Is User Authentication is successful: " + val);
})
);
}
logout(): void {
this.isUserLoggedIn = false;
localStorage.removeItem('isUserLoggedIn');
}
constructor() { }
}
這裡,
我們編寫了兩個方法,login 和 logout。
login 方法的目的是驗證使用者,如果使用者成功驗證,則將其資訊儲存在localStorage中,然後返回 true。
身份驗證驗證是使用者名稱和密碼應為admin。
我們沒有使用任何後端。相反,我們使用 Observables 模擬了 1 秒的延遲。
logout 方法的目的是使使用者無效並刪除儲存在localStorage中的資訊。
使用以下命令建立login元件:
ng generate component login CREATE src/app/login/login.component.html (20 bytes) CREATE src/app/login/login.component.spec.ts (621 bytes) CREATE src/app/login/login.component.ts (265 bytes) CREATE src/app/login/login.component.css (0 bytes) UPDATE src/app/app.module.ts (1207 bytes)
開啟LoginComponent幷包含以下程式碼:
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormControl } from '@angular/forms';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
userName: string;
password: string;
formData: FormGroup;
constructor(private authService : AuthService, private router : Router) { }
ngOnInit() {
this.formData = new FormGroup({
userName: new FormControl("admin"),
password: new FormControl("admin"),
});
}
onClickSubmit(data: any) {
this.userName = data.userName;
this.password = data.password;
console.log("Login page: " + this.userName);
console.log("Login page: " + this.password);
this.authService.login(this.userName, this.password)
.subscribe( data => {
console.log("Is Login Success: " + data);
if(data) this.router.navigate(['/expenses']);
});
}
}
這裡,
使用響應式表單。
匯入 AuthService 和 Router 並將其配置在建構函式中。
建立 FormGroup 的例項幷包含兩個 FormControl 的例項,一個用於使用者名稱,另一個用於密碼。
建立 onClickSubmit 來使用 authService 驗證使用者,如果成功,則導航到費用列表。
開啟LoginComponent模板幷包含以下模板程式碼。
<!-- Page Content -->
<div class="container">
<div class="row">
<div class="col-lg-12 text-center" style="padding-top: 20px;">
<div class="container box" style="margin-top: 10px; padding-left: 0px; padding-right: 0px;">
<div class="row">
<div class="col-12" style="text-align: center;">
<form [formGroup]="formData" (ngSubmit)="onClickSubmit(formData.value)" class="form-signin">
<h2 class="form-signin-heading">Please sign in</h2>
<label for="inputEmail" class="sr-only">Email address</label>
<input type="text" id="username" class="form-control" formControlName="userName" placeholder="Username" required autofocus>
<label for="inputPassword" class="sr-only">Password</label>
<input type="password" id="inputPassword" class="form-control"
formControlName="password" placeholder="Password" required>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
</form>
</div>
</div>
</div>
</div>
</div>
</div>
這裡,
建立了一個響應式表單並設計了一個登入表單。
將onClickSubmit方法附加到表單提交操作。
開啟LoginComponent樣式幷包含以下 CSS 程式碼。
.form-signin {
max-width: 330px;
padding: 15px;
margin: 0 auto;
}
input {
margin-bottom: 20px;
}
這裡,添加了一些樣式來設計登入表單。
使用以下命令建立登出元件:
ng generate component logout CREATE src/app/logout/logout.component.html (21 bytes) CREATE src/app/logout/logout.component.spec.ts (628 bytes) CREATE src/app/logout/logout.component.ts (269 bytes) CREATE src/app/logout/logout.component.css (0 bytes) UPDATE src/app/app.module.ts (1368 bytes)
開啟LogoutComponent幷包含以下程式碼:
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';
@Component({
selector: 'app-logout',
templateUrl: './logout.component.html',
styleUrls: ['./logout.component.css']
})
export class LogoutComponent implements OnInit {
constructor(private authService : AuthService, private router: Router) { }
ngOnInit() {
this.authService.logout();
this.router.navigate(['/']);
}
}
這裡,
- 使用 AuthService 的登出方法。
- 使用者登出後,頁面將重定向到主頁(/)。
使用以下命令建立守衛:
ng generate guard expense CREATE src/app/expense.guard.spec.ts (364 bytes) CREATE src/app/expense.guard.ts (459 bytes)
開啟 ExpenseGuard 幷包含以下程式碼:
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router, UrlTree } from '@angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
@Injectable({
providedIn: 'root'
})
export class ExpenseGuard implements CanActivate {
constructor(private authService: AuthService, private router: Router) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): boolean | UrlTree {
let url: string = state.url;
return this.checkLogin(url);
}
checkLogin(url: string): true | UrlTree {
console.log("Url: " + url)
let val: string = localStorage.getItem('isUserLoggedIn');
if(val != null && val == "true"){
if(url == "/login")
this.router.parseUrl('/expenses');
else
return true;
} else {
return this.router.parseUrl('/login');
}
}
}
這裡,
- checkLogin 將檢查 localStorage 是否包含使用者資訊,如果可用,則返回 true。
- 如果使用者已登入並轉到登入頁面,它將把使用者重定向到費用頁面
- 如果使用者未登入,則使用者將被重定向到登入頁面。
開啟AppRoutingModule (src/app/app-routing.module.ts)並更新以下程式碼:
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { ExpenseEntryComponent } from './expense-entry/expense-entry.component';
import { ExpenseEntryListComponent } from './expense-entry-list/expense-entry-list.component';
import { LoginComponent } from './login/login.component';
import { LogoutComponent } from './logout/logout.component';
import { ExpenseGuard } from './expense.guard';
const routes: Routes = [
{ path: 'login', component: LoginComponent },
{ path: 'logout', component: LogoutComponent },
{ path: 'expenses', component: ExpenseEntryListComponent, canActivate: [ExpenseGuard]},
{ path: 'expenses/detail/:id', component: ExpenseEntryComponent, canActivate: [ExpenseGuard]},
{ path: '', redirectTo: 'expenses', pathMatch: 'full' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule { }
這裡,
- 匯入 LoginComponent 和 LogoutComponent。
- 匯入 ExpenseGuard。
- 建立了兩個新路由,login 和 logout,分別用於訪問 LoginComponent 和 LogoutComponent。
- 為 ExpenseEntryComponent 和 ExpenseEntryListComponent 新增新的選項 canActivate。
開啟AppComponent模板並新增兩個登入和登出連結。
<div class="collapse navbar-collapse" id="navbarResponsive">
<ul class="navbar-nav ml-auto">
<li class="nav-item active">
<a class="nav-link" href="#">Home
<span class="sr-only" routerLink="/">(current)</span>
</a>
</li>
<li class="nav-item">
<a class="nav-link" routerLink="/expenses">Report</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Add Expense</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">About</a>
</li>
<li class="nav-item">
<div *ngIf="isUserLoggedIn; else isLogOut">
<a class="nav-link" routerLink="/logout">Logout</a>
</div>
<ng-template #isLogOut>
<a class="nav-link" routerLink="/login">Login</a>
</ng-template>
</li>
</ul>
</div>
開啟AppComponent並更新以下程式碼:
import { Component } from '@angular/core';
import { AuthService } from './auth.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Expense Manager';
isUserLoggedIn = false;
constructor(private authService: AuthService) {}
ngOnInit() {
let storeData = localStorage.getItem("isUserLoggedIn");
console.log("StoreData: " + storeData);
if( storeData != null && storeData == "true")
this.isUserLoggedIn = true;
else
this.isUserLoggedIn = false;
}
}
在這裡,我們添加了識別使用者狀態的邏輯,以便我們可以顯示登入/登出功能。
開啟AppModule (src/app/app.module.ts)並配置ReactiveFormsModule
import { ReactiveFormsModule } from '@angular/forms';
imports: [
ReactiveFormsModule
]
現在,執行應用程式,應用程式將開啟登入頁面。
輸入 admin 和 admin 作為使用者名稱和密碼,然後單擊提交。應用程式處理登入並將使用者重定向到費用列表頁面,如下所示:
最後,您可以單擊登出並退出應用程式。