- Angular 8 教程
- Angular 8 - 主页
- Angular 8 - 简介
- Angular 8 - 安装
- 创建第一个应用程序
- Angular 8 - 架构
- Angular 组件和模板
- Angular 8 - 数据绑定
- Angular 8 - 指令
- Angular 8 - 管道
- Angular 8 - 响应式编程
- 服务和依赖注入
- Angular 8 - Http 客户端编程
- Angular 8 - 角度材料
- 路线和导航
- Angular 8 - 动画
- Angular 8 - 表单
- Angular 8 - 表单验证
- 认证与授权
- Angular 8 - 网络工作者
- Service Worker 和 PWA
- Angular 8 - 服务器端渲染
- Angular 8 - 国际化 (i18n)
- Angular 8 - 辅助功能
- Angular 8 - CLI 命令
- Angular 8 - 测试
- Angular 8 - Ivy 编译器
- Angular 8 - 使用 Bazel 构建
- Angular 8 - 向后兼容性
- Angular 8 - 工作示例
- Angular 9 - 有什么新变化?
- Angular 8 有用资源
- Angular 8 - 快速指南
- Angular 8 - 有用的资源
- Angular 8 - 讨论
Angular 8 - 身份验证和授权
身份验证是将 Web 应用程序的访问者与系统中预定义的用户身份集进行匹配的过程。换句话说,就是识别用户身份的过程。就安全性而言,身份验证是系统中非常重要的过程。
授权是授予用户访问系统中某些资源的权限的过程。只有经过身份验证的用户才能被授权访问资源。
本章让我们学习如何在 Angular 应用程序中进行身份验证和授权。
路由中的守卫
在 Web 应用程序中,资源通过 url 引用。系统中的每个用户都将被允许访问一组网址。例如,管理员可能会被分配管理部分下的所有 url。
正如我们所知,URL 是由Routing处理的。Angular 路由可以根据编程逻辑对 URL 进行保护和限制。因此,普通用户可能会拒绝某个 URL,而管理员可能会允许该 URL。
Angular 提供了一个称为Router Guards的概念,可用于防止通过路由对应用程序的某些部分进行未经授权的访问。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方法的目的是验证用户,如果用户验证成功,它将信息存储在localStorage中,然后返回true。
认证验证是用户名和密码应该是admin。
我们没有使用任何后端。相反,我们使用 Observables 模拟了 1 秒的延迟。
logout方法的目的是使用户失效并删除localStorage中存储的信息。
使用以下命令创建登录组件 -
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。
- 创建了两个新路由,登录和注销,分别访问 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 作为用户名和密码,然后单击提交。应用程序处理登录并将用户重定向到费用列表页面,如下所示 -
最后,您可以单击注销并退出应用程序。