前言
延續上一篇的情境,現在有了前台與後台的區分。但實務上後台是不允許未經認證的人隨意進入瀏覽的,通常會搭配一個登入介面,而光靠登入介面還不夠,因為使用者很可能會藉由直接輸入網址的方式瀏覽後台頁面,這時候就需要依賴路由守衛 (Route Guards) 了 。
需求
延續上一份 ngRouteDemo 的範例,現在需求為:
- 前台 (front) - 一般人可以隨意瀏覽的部分
- index
- page1
- page2
- page3
- index
- 後台 (back) - 希望加入路由守衛的部分
- index
- page1
- page2
- page3
- index
範例參考中文 Angular 官方手冊
路由守衛 (Route Guards)
根據官方文件說明,路由守衛被應用的場合可能會是:
- 該使用者可能無權導航到目標元件
- 可能使用者得先登入(認證)
- 在顯示目標元件前,可能得先獲取某些資料。
- 在離開元件前,你可能要先儲存修改。
- 你可能要詢問使用者:你是否要放棄本次更改,而不用儲存它們?
可以藉由路由配置中新增守衛,來處理以上這些應用場合。
路由守衛的返回值
路由守衛會返回一個值,以控制路由器的行為:
- 如果它返回
true
導航過程會繼續 - 如果它返回
false
導航過程就會終止,且使用者留在原地- 當返回
false
時,也可以告訴路由器導航到別處,例如可以導航到登入頁面
- 當返回
- 如果它返回 UrlTree 則取消當前的導航,並且開始導航到返回的這個 UrlTree
路由守衛的介面種類
路由器可以支援多種守衛介面:
- CanActivate - 處理導航到某路由的情況
- CanActivateChild - 處理導航到某子路由的情況
- CanDeactivate - 來處理從當前路由離開的情況
- Resolve - 在路由啟用之前獲取路由資料
- CanLoad - 處理非同步導航到某特性模組的情況
路由守衛檢查的順序
根據手冊上的描述,在分層路由的每個級別上,可以設定多個守衛。
- 路由器會先按照從最深的子路由,由下往上檢查的順序來檢查 CanDeactivate() 和 CanActivateChild() 守衛
- 然後它會按照從上到下的順序檢查 CanActivate() 守衛。
- 如果特性模組是非同步載入的,在載入它之前還會檢查 CanLoad() 守衛。
如果任何一個守衛返回 false
,其它尚未完成的守衛會被取消,這樣整個導航就被取消了。
這樣看起來最適合這個範例使用的應該就是 CanActivate 了。
CanActivate
所以我們要使用 CanActivate 來保護後台的所有頁面不被未授權的使用者看到。
但是在這個範例中,為了方便示範:
- 將使用 CanActivate 並把路由守衛回傳值設為
false
- 藉以證明無法透過輸入網址進入保護頁面
建立路由守衛
輸入指令,以下兩種指令則一即可
1 | ng generate guard auth/auth |
1 | ng g g auth/auth |
輸入後 Angular CLI 也會很貼心的問我們要使用哪一種
選擇 CanActivate 後按下 Enter ,發現 Angular CLI 建立了兩隻檔案,分別為測試檔以及主要檔案。
觀察 auth.guard
這支由 Angular CLI 建立的檔案,剛打開時什麼都沒有,而且還會跳出錯誤。
1 | import { Injectable } from '@angular/core'; |
顯然的需要在這裡寫一些東西才行。
於是可以先使用官方提供的範例,觀察一下運作情形,稍後再來修改成我們要的。
- 從
@angular/router
中 importCanActivate
- 貼上官方的範例程式
auth.guard
1 | import { Injectable } from '@angular/core'; |
這樣基本的路由守衛配置就完成了。
匯入路由守衛
接著把剛才設定好的路由守衛匯入 back-routing.module 。
1 | import { NgModule } from '@angular/core'; |
搞定,先運行看看吧!
成功了,接著我們回過頭來研究 auth.guard.ts 檔內的 canActivate() 吧!
調整 auth.guard 內的 canActivate()
可以參考官方提供的文件
1 | import { Injectable } from '@angular/core'; |
- ActivatedRouteSnapshot 包含了即將被啟用的路由
- RouterStateSnapshot 包含了該應用即將到達的狀態
為了使未認證的使用者能導航到指定頁面,我們需要匯入 Router
類別,並且在建構式內實例化,接著使用底下的方法 navigate()
。
強制返回 false
,並且觀察當觸發路由守衛時,是否會正確的導航到指定頁面。
搞定了!測試看看吧。
嘗試輸入後台子頁面的網址仍被路由守衛攔下,由於畫面相同就不重複張貼了。
小結
於是靠著官方手冊的指引,我們成功地建立了一個路由守衛,使得未通過驗證的人不得訪問後台的網頁。
當然這部分省略了很多東西,但我目的僅為了實作一個簡單的路由守衛範例,並驗證它是真的有效,所以就省略了登入步驟,直接設定不管如何都是 false
,藉以觀察當未通過驗證時是否正確導航到指定目的地。