在 Angular 5 应用中实现 Guard






4.67/5 (5投票s)
在这篇文章中,我们将实现 Guard,它将帮助我们在 Angular 5 应用中限制用户在未登录的情况下访问某些页面。
引言
这篇文章是课程“开发 Angular 5 应用”系列的延续。如果您还没有阅读过前面的文章,我强烈建议您先阅读。您可以在下方找到前一篇文章的链接。在这篇文章中,我们将实现 Guard,它将帮助我们在 Angular 5 应用中限制用户在未登录的情况下访问某些页面。因此,在文章结束时,您将了解到如何处理未经授权的路由访问。希望您会喜欢这篇文章。
开发 Angular 5 应用系列
这些是本系列的上一篇文章。请继续阅读。
- 什么是新的,以及如何设置我们的第一个 Angular 5 应用程序
- Angular 5 基本演示项目概述
- 在 Angular 5 应用中生成你的第一个组件和模块
- 在 Angular 5 应用中实现验证
背景
我们的应用可能有很多页面,有些页面可能供已登录用户使用,有些页面供未经授权的用户使用。但是,如果您允许所有用户使用所有页面,而不管他们是否已登录?是的,这显然不是一个完美的应用程序。在我们的应用程序中,我们将只允许已登录用户访问主页。要实现这一点,我们可以使用 Guard。
生成 Guard
由于我们正在使用 Angular CLI,我们可以轻松地在我们的应用程序中生成 Auth Guard。您还记得我们在上一篇文章中创建了一些组件吗?我们可以用同样的方式创建 Guard。
PS F:\Visual Studio\ng5> ng generate guard auth
在上面的步骤中,我们在根级别生成了 Guard。运行上述命令后,它将生成两个新的 Typescript 文件,如下所示:
现在让我们来看看这两个文件。
auth.guard.ts
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot }
from '@angular/router';
import { Observable } from 'rxjs/Observable';
@Injectable()
export class AuthGuard implements CanActivate {
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
return true;
}
}
auth.guard.spec.ts
import { TestBed, async, inject } from '@angular/core/testing';
import { AuthGuard } from './auth.guard';
describe('AuthGuard', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [AuthGuard]
});
});
it('should ...', inject([AuthGuard], (guard: AuthGuard) => {
expect(guard).toBeTruthy();
}));
});
这只是一个测试文件,我们将在另一篇文章中介绍如何在 Angular 应用上执行测试。现在,让我们专注于实现。
生成登录组件并进行设置
现在 Guard 已经准备好进行修改了,在进行修改之前,让我们创建一个登录组件。
PS F:\Visual Studio\ng5> ng g c login
create src/app/login/login.component.html (24 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 (1331 bytes)
PS F:\Visual Studio\ng5>
现在是时候在 app.module.ts 文件中编辑我们的路由,为登录添加新路由了。请注意,在 app.module.ts 文件中创建路由不是推荐的方式。我们应该为此创建一个单独的路由模块,并在 app.module.ts 中使用它。我将在另一篇文章中分享如何做到这一点。
import { LoginComponent } from './login/login.component';
const myRoots: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' },
{ path: 'register', component: RegistrationComponent },
{ path: 'login', component: LoginComponent},
{ path: 'home', component: HomeComponent}]
@NgModule({
declarations: [
AppComponent,
HomeComponent,
NavComponent,
RegistrationComponent,
LoginComponent
],
imports: [
BrowserModule, BrowserAnimationsModule, FormsModule, ReactiveFormsModule,
MatButtonModule, MatCardModule, MatInputModule, MatSnackBarModule, MatToolbarModule,
RouterModule.forRoot(myRoots)
],
providers: [],
bootstrap: [AppComponent]
})
我们可能还需要通过添加一个用于登录的新按钮来编辑 nav.component.html 文件。
<mat-toolbar color="primary">
<button mat-button routerLink="/">Home</button>
<button mat-button routerLink="/register">Register</button>
<button mat-button routerLink="/login">Login</button>
</mat-toolbar>
现在,转到您的 login.component.html 文件并添加以下标记。
<mat-card>
<form [formGroup]="form" (ngSubmit)="login()">
<mat-input-container>
<input matInput type="email" placeholder="Email" formControlName="email" />
</mat-input-container>
<mat-input-container>
<input matInput type="password" placeholder="Password" formControlName="password" />
</mat-input-container>
<button mat-raised-button type="submit" color="primary">Login</button>
</form>
</mat-card>
如您所见,我们在提交时实际上调用了一个 login 方法。在我们实现该函数之前,现在让我们在 login.component.ts 文件中创建一个 auth 服务并进行如下编辑:
import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
@Injectable()
export class AuthService {
constructor(private myRoute: Router) { }
sendToken(token: string) {
localStorage.setItem("LoggedInUser", token)
}
getToken() {
return localStorage.getItem("LoggedInUser")
}
isLoggednIn() {
return this.getToken() !== null;
}
logout() {
localStorage.removeItem("LoggedInUser");
this.myRoute.navigate(["login"]);
}
}
现在是时候编辑我们的 login.component.ts 文件了。
import { Component, OnInit } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-login',
templateUrl: './login.component.html',
styleUrls: ['./login.component.css']
})
export class LoginComponent implements OnInit {
form;
constructor(private fb: FormBuilder,
private myRoute: Router,
private auth: AuthService) {
this.form = fb.group({
email: ['', [Validators.required, Validators.email]],
password: ['', Validators.required]
});
}
ngOnInit() {
}
login() {
if (this.form.valid) {
this.auth.sendToken(this.form.value.email)
this.myRoute.navigate(["home"]);
}
}
}
这不是首选的实现方式,相反,您应该创建一个 API 服务并连接到数据库。然后,检查此电子邮件和密码是否有效。最后,您应该使用从服务器 API 获得的令牌来设置
localStorage
。这个主题足够写另一篇文章了,为了使这篇文章简短,我只在这里设置 email 的值。
如果您收到错误“ERROR Error: Uncaught (in promise): EmptyError: no elements in sequence”,请确保您的 rxjs 版本在 package.json 文件中大于或等于 5.5.4。rxjs 版本 5.5.3 在 Angular 5 中存在此问题。
现在运行您的应用程序,并确保它被重定向到主页。您也可以在浏览器控制台中检查 localStorage
条目。
设置生成的 Guard
让我们转到我们的 auth.guard.ts 文件并进行一些更改。
import { Injectable } from '@angular/core';
import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot }
from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { AuthService } from './auth.service';
import {Router} from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService,
private myRoute: Router){
}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if(this.auth.isLoggednIn()){
return true;
}else{
this.myRoute.navigate(["login"]);
return false;
}
}
}
不要忘记在 app.module.ts 的 providers 中添加 AuthService
和 AuthGuard
。
import { AuthService } from './auth.service';
import { AuthGuard } from './auth.guard';
在路由中使用 Auth Guard
太棒了,设置完成了,现在是时候行动了。让我们转到我们的路由并按照以下方式更改代码:
const myRoots: Routes = [
{ path: '', component: HomeComponent, pathMatch: 'full' , canActivate: [AuthGuard]},
{ path: 'register', component: RegistrationComponent },
{ path: 'login', component: LoginComponent},
{ path: 'home', component: HomeComponent, canActivate: [AuthGuard]}
];
运行您的应用程序,并在登录前尝试访问主页。如果一切顺利,它应该会将您重定向到登录页面。
让我们在 nav.component.html 和 nav.component.ts 中进行更多编码,以使我们的应用程序更有意义。
nav.component.ts
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
@Component({
selector: 'app-nav',
templateUrl: './nav.component.html',
styleUrls: ['./nav.component.css']
})
export class NavComponent implements OnInit {
constructor(private auth: AuthService) { }
ngOnInit() {
}
}
nav.component.html
<mat-toolbar color="primary">
<button mat-button routerLink="/home">Home</button>
<button mat-button *ngIf="!auth.isLoggednIn()"
routerLink="/register">Register</button>
<button mat-button *ngIf="!auth.isLoggednIn()"
routerLink="/login">Login</button>
<button mat-button *ngIf="auth.isLoggednIn()"
(click)="auth.logout()">Logout</button>
</mat-toolbar>
在这里,我们只为已登录用户提供“登出”按钮,并隐藏已登录用户的“注册”和“登录”按钮。听起来不错吧?由于我们在 nav.component.ts 文件中注入了 auth 服务,因此 auth.isLoggedIn
函数将在此页面上可用。
结论
非常感谢你的阅读。我是否遗漏了你认为需要的东西?你觉得这篇文章有用吗?我希望你喜欢这篇文章。请分享你宝贵的建议和反馈。
现在轮到你了。你有什么想法?
没有评论的文章 isn't an article,但请尽量就事论事。如果您有与本文无关的问题,最好将其发布到 C# Corner、Code Project、Stack Overflow、ASP.NET Forum,而不是在这里评论。请在下面的评论部分发布您的问题,如果我能帮忙,我一定会尽力而为。
历史
- 2018年3月25日:初始版本