在每一個嚴肅的應用中,認證和授權都是非常重要的一個部分。單頁應用也不例外。應用并不會将所有的資料和功能都 暴露給所有的使用者。使用者需要通過認證和授權來檢視應用的某個特定部分,或者在應用中進行特定的行為。為了在應用中對使用者進行識别,我們需要讓使用者進行登入。
在使用者管理方面,傳統的伺服器端應用和單頁應用的實作方式有所不同,單頁應用能夠和伺服器通信的方式隻有AJAX。對于登入和退出來說也是如此。
負責識别使用者的伺服器端需要暴露出一個認證斷電。單頁應用将會把使用者輸入的資訊發送到這個節點進行認證。在一個基于認證系統的典型token中,這 項服務用于在認證完畢之後擷取一個token或者一個包含已登入使用者的名字和角色資訊的對象。用戶端則需要在所有的安全API中擷取這個token。
由于擷取toekn的行為将會多次發生,我們最好将這個token存在用戶端。在Angular中,我們可以将這個值存在一個服務中,因為服務在客 戶端中是一個單體。但是,如果使用者重新整理了頁面,服務中的值将會丢失。在這種情況下,最好将值存放在一個有浏覽器提供的安全存儲中,在這裡我們要是用的是 sessionStorage,因為它在浏覽器關閉時會自動被清空。
我們現在來看一些代碼。假設我們已經實作了所有的伺服器端的邏輯,并且有一個叫做<code>api/login</code>的REST接口進行登入認證,它将傳回一個token。我們來寫一個簡單的服務用于使用者登入。在後面我們會為這個服務逐漸添加功能:
在實際的代碼中,你可能會想要将存儲的代碼重構為一個單獨的服務。在這裡為了簡單起見,我們隻是将它放在他用一個服務中。這個服務可以被一個用于處理登入功能的控制器所用。
我們需要在應用中設定一些安全路由。如果一個使用者沒有登入同時想要進入到某一個安全路由中,他應該被重定向到登入頁。我們可以使用路由選項中的<code>resolve</code>來實作這個功能。下面的代碼片段展示了其中一種實作思路:
<code>resolve</code>塊可以包含多個代碼塊,這些代碼塊将會在完成時傳回promise對象。為了說明,上面代碼中的<code>auth</code>并不在架構中,而是我們自己定義的。你可以根據你的需求來進行修改。
通過或者拒絕路由的原因有很多種。在這裡的情形中,你可以在解析/拒絕一個promise的時候傳遞一個對象。我們在服務中還沒有實作<code>getLoggedInUser()</code>方法。它是一個很簡單的方法,能夠從服務中傳回<code>loggedInUser</code>對象。
通過上面的代碼中的promise發送的想将會通過$rootScope進行廣播。如果路由被解析,那麼$routeChangeSuccess事件将會 被廣播。然而,如果路由失敗,那麼事件$touteChangeError将會被廣播。我們将監聽$routeChangeError事件并将使用者重定向 到登入頁上。由于事件是在$rootScope層級上,最好在<code>run</code>函數中綁定事件處理器。
當使用者重新整理頁面時,服務将會失去現有狀态。我們需要從浏覽器的session storage中擷取資料并将這些值指派給loggerInUser變量。由于一個factory隻會被調用一次,我們需要在一個初始化函數中設定這個變量,代碼如下所示:
當使用者想要從應用中退出時,相應的API必須連同包含在請求頭部中的token一起被調用。一旦使用者退出,我們還需要清空sessionstorage中的資料。下面例子包含了一個退出函數,這個函數需要被添加到認證服務中。
<code></code>
單頁應用的認證方式和傳統web應用的認證方式非常不同。由于主要的工作都搬到了浏覽器端,使用者的狀态也需要存儲在用戶端。重要的一點是要記住使用者的狀态也需要的伺服器端儲存和進行驗證,因為駭客很可能慧聰用戶端竊取使用者的資料。