Angular给我们提供了很强大的安全守卫。
CanActivate
使用
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中。 当然,我们肯定需要一些验证,在这里,我们使用`localStorage`来设置用户是否登录。
修改一下代码:
// 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: [AuthGuard]`,这表示安全路由检测可以多个,并且只有当所有都返回true时,才通过;也就是说,只要有一个返回false,则不通。
CanActivateChild
CanActivate只是保护当前路由,而不会检测其子路由(如果有的话)。 要给其所有子路由添加安全守卫,我们可以使用CanActivateChild。 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 { }
CanDeactivate有些情况(比如个人信息修改页面),我们需要监听用户是否修改了某些信息,然后当用户离开时,我们需要做一些提示,这时我们就需要监听到用户啥时离开了。
在Angular中,我们使用
CanDeactivate守卫。 创建一个守卫: // 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中要比较的值。 如有疑问或更好建议,欢迎在下方评论区留言!