Angular4 开发实战:(12) 安全路由
Angular CLI,我们可以创建一个基础安全守卫:
ng g guard auth
基础模板如下:
// app/guard/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;
}
}
当canActivate()返回true,表示可以进入指定路由;否则停留到当前页面。canActivate()可以接受两个可选的参数:
ActivateRouteSnapshot:将被激活的路由RouterStateSnapshot:未来的路由状态
// app/modules/app-routing.module.ts
import {AuthGuard} from '../guard/auth.guard';
const routes: Routes = [
{ path: 'images',
canActivate: [AuthGuard],
component: DemoImageComponent}
];
@NgModule({
providers: [AuthGuard]
})
export class AppRoutingModule { }
在上面的代码中,我们给路由images添加了一个安全守卫canActivate: [AuthGuard]。
AuthGuard注册到模块的提供商providers中。// app/guard/auth.guard.ts
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
if (localStorage.getItem('user')) {
return true;
} else {
return false;
}
}
新增页面:
// app/demo/demo-guard.component.html
<p>账号是123,密码也是123</p>
<p>
<input type="text" #account>
<input type="password" #pwd>
<button (click)="onSubmit(account.value, pwd.value)">登录</button>
</p>
<a routerLink="/images">这是个照片链接,你懂得。</a>
// app/demo/demo-guard.component.ts
onSubmit(account: string, pwd: string) {
if (account === '123' && pwd === '123') {
localStorage.setItem('user', JSON.stringify({account: account, password: pwd}));
alert('登录成功,你可以看图片了,嘿嘿');
}
}
在上面的代码中,提供了账号和密码的输入框,只有输入正确的账号和密码,才可以提交登录,然后设置一个localStorage,也就是user。CanActivate只是保护当前路由,而不会检测其子路由(如果有的话)。
要给其所有子路由添加安全守卫,我们可以使用CanActivateChild。CanActivate类似,两者的区别在于前者会在其任意一个子路由变化的时候检测。
import { Injectable } from '@angular/core';
import {
CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot, CanActivateChild
} from '@angular/router';
@Injectable()
export class AuthGuard implements CanActivate, CanActivateChild {
/* . . . */
canActivateChild(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
return this.canActivate();
}
}
使用方式也和CanActivate一样要添加到提供商中(provider),然后使用:
// src/app/app.module.ts
const routes: Routes = [
{
path: 'demoGuard',
component: DemoGuardComponent,
children: [
{
path: '',
canActivateChild: [AuthChildGuard],
children: [
{path: 'child', component: DemoGuardChildComponent}
]
}
]
}
];
@NgModule({
imports: [...],
exports: [...],
providers: [AuthGuard, AuthChildGuard]
})
export class AppRoutingModule { }
CanDeactivateCanDeactivate守卫。
创建一个守卫:
// src/app/guard/canDeactivateGuard.ts
import {ActivatedRouteSnapshot, CanDeactivate, RouterStateSnapshot} from '@angular/router';
import {Injectable} from '@angular/core';
import {DemoGuardComponent} from '../demo/demo-guard/demo-guard.component';
@Injectable()
export class CanDeactivateGuard implements CanDeactivate<DemoGuardComponent> {
oldName: string = '123';
canDeactivate(component: DemoGuardComponent,
route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Promise<boolean> | boolean {
if (this.oldName === component.name) {
return true;
}
return confirm('信息未保存,确认要离开!');
}
}
在上面的代码中,oldName是假设原值,用来和DemoGuardComponent组件(这也是当前页面的组件)中的name值进行比较,如果相等,表示值未修改,可直接跳转;如修改了,则弹出提示框。
接着再增加以下代码:
// src/app/demo/demo-guard.component.html
<div class="box">
<h4> CanDeactivateGuard</h4>
<p>
<input type="text" [(ngModel)]="name"> (尝试修改表单的内容,然后点击其他页面)
</p>
</div>
// src/app/demo/demo-guard.component.ts
export class DemoGuardComponent implements OnInit {
name: string = '123';
}
在上面,我们使用NgModel来双向绑定到属性name上,也就是安全守卫CanDeactivateGuard中要比较的值。