- 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 作为用户名和密码,然后单击提交。应用程序处理登录并将用户重定向到费用列表页面,如下所示:
最后,您可以单击注销并退出应用程序。