场景:
项目中,有时我们会发现当我们通过修改地址栏中URL,比如改变访问的id,这时页面就会跳转到新请求的页面。那如果该页面的内容是当前用户无法看到的,对于后台来讲,如果链接的层级嵌套较深(即子路由层级很多, 那么后台想要阻止这种情况的权限认证是非常麻烦的,最好是通过前端路由拦截,让用户当手动修改浏览器URL或者点击前进后退操作时,系统直接跳转到登陆界面,以此逻辑来保证用户信息的安全性。
分析:
想要解决这个问题,首先我们需要搞清楚如何能够监测到用户对浏览器手动修改的操作和浏览器前进后退功能的触发;然后当这些事件被监测到以后,我们就将页面重定向到登录界面即可。
解决方案:
1. 通过研究我们发现window对象实现了WindowEventHandlers mixin下面的onPopState方法, 这个方法就可以监听到浏览器前进/后退操作。在Angular中class PlatformLocation类似的实现了onPopState, pushState, replaceState, forward, back等方法。因此可以在顶层组件中注入PlatformLocation。如下使用方式:
import { PlatformLocation } from '@angular/common'; constructor( private location: PlatformLocation
){ this.location.onPopState(() => { this.init.cnRouterLinkFlag = false; // this.init.cnRouterLinkFlag 作为一个共享变量,用来记录地址栏中的url是否被修改过 }); }
2. 然后通过路由守卫在路由跳转时更改 this.init.cnRouterLinkFlag 标志值。对于本系统中的路由链接,当我们登录进来以后我们就把 this.init.cnRouterLinkFlag 设置为true。然后通过路由守卫在每一个路由处去判断 this.init.cnRouterLinkFlag 标志值。 如果标志值是true就允许路由跳转,否则拒绝该路由跳转,系统跳回最初登录界面。
// redirectToLogin.service.ts 顶层路由守卫,用来设置刚进入系统的flag import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Initial } from './initial'; @Injectable({ providedIn: 'root' }) export class RedirectToLoginService implements CanActivate { constructor( private router: Router, private init: Initial, ) {} canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> | Promise<boolean> | boolean { console.log('RedirectToLoginService cnRouterLinkFlag:'+ this.init.cnRouterLinkFlag); if (this.init.cnRouterLinkFlag === undefined) { this.init.cnRouterLinkFlag = true; } // if (this.router.url === '/') { // this.router.navigate(['login']); // return false; // } return true; } }
// 子路由守卫,用来通过标志符判断是否路由 import { Injectable } from '@angular/core'; import { CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { Observable } from 'rxjs/Observable'; import { Initial } from './initial'; @Injectable({ providedIn: 'root' }) export class RouterLinkRedirectService implements CanActivate { constructor( private router: Router, private init: Initial, ) {} canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot):Observable<boolean> | Promise<boolean> | boolean { console.log('CNRouterLinkFlag is : ' + this.init.cnRouterLinkFlag); if (!this.init.cnRouterLinkFlag) { this.router.navigate(['login']); return false; } if (this.init.cnRouterLinkFlag) { return true; } } }