場景:
項目中,有時我們會發現當我們通過修改地址欄中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; } } }