標籤:des blog http io os ar 使用 java for
在AngularJS應用中實現認證授權
在每一個嚴肅的應用中,認證和授權都是非常重要的一個部分。單頁應用也不例外。應用並不會將所有的資料和功能都 暴露給所有的使用者。使用者需要通過認證和授權來查看應用的某個特定部分,或者在應用中進行特定的行為。為了在應用中對使用者進行識別,我們需要讓使用者進行登入。
在使用者管理方面,傳統的伺服器端應用和單頁應用的實現方式有所不同,單頁應用能夠和伺服器通訊的方式只有AJAX。對於登入和退出來說也是如此。
負責識別使用者的伺服器端需要暴露出一個認證斷電。單頁應用將會把使用者輸入的資訊發送到這個節點進行認證。在一個基於認證系統的典型token中,這 項服務用於在認證完畢之後擷取一個token或者一個包含已登入使用者的名字和角色資訊的對象。用戶端則需要在所有的安全API中擷取這個token。
由於擷取toekn的行為將會多次發生,我們最好將這個token存在用戶端。在Angular中,我們可以將這個值存在一個服務中,因為服務在客 戶端中是一個單體。但是,如果使用者重新整理了頁面,服務中的值將會丟失。在這種情況下,最好將值存放在一個有瀏覽器提供的安全儲存中,在這裡我們要是用的是 sessionStorage,因為它在瀏覽器關閉時會自動被清空。
實現登入
我們現在來看一些代碼。假設我們已經實現了所有的伺服器端的邏輯,並且有一個叫做api/login的REST介面進行登入認證,它將返回一個token。我們來寫一個簡單的服務用於使用者登入。在後面我們會為這個服務逐漸添加功能:
app.factory("authenticationSvc", function($http, $q, $window) { var userInfo; function login(userName, password) { var deferred = $q.defer(); $http.post("/api/login", { userName: userName, password: password }).then(function(result) { userInfo = { accessToken: result.data.access_token, userName: result.data.userName }; $window.sessionStorage["userInfo"] = JSON.stringify(userInfo); deferred.resolve(userInfo); }, function(error) { deferred.reject(error); }); return deferred.promise; } return { login: login };});
在實際的代碼中,你可能會想要將儲存的代碼重構為一個單獨的服務。在這裡為了簡單起見,我們只是將它放在他用一個服務中。這個服務可以被一個用於處理登入功能的控制器所用。
安全路由
我們需要在應用中設定一些安全路由。如果一個使用者沒有登入同時想要進入到某一個安全路由中,他應該被重新導向到登入頁。我們可以使用路由選項中的resolve來實現這個功能。下面的程式碼片段展示了其中一種實現思路:
$routeProvider.when("/", { templateUrl: "templates/home.html", controller: "HomeController", resolve: { auth: ["$q", "authenticationSvc", function($q, authenticationSvc) { var userInfo = authenticationSvc.getUserInfo(); if (userInfo) { return $q.when(userInfo); } else { return $q.reject({ authenticated: false }); } }] }});
resolve塊可以包含多個代碼塊,這些代碼塊將會在完成時返回promise對象。為了說明,上面代碼中的auth並不在架構中,而是我們自己定義的。你可以根據你的需求來進行修改。
通過或者拒絕路由的原因有很多種。在這裡的情形中,你可以在解析/拒絕一個promise的時候傳遞一個對象。我們在服務中還沒有實現getLoggedInUser()方法。它是一個很簡單的方法,能夠從服務中返回loggedInUser對象。
app.factory("authenticationSvc", function() { var userInfo; function getUserInfo() { return userInfo; }});
通過上面的代碼中的promise發送的想將會通過$rootScope進行廣播。如果路由被解析,那麼$routeChangeSuccess事件將會 被廣播。然而,如果路由失敗,那麼事件$touteChangeError將會被廣播。我們將監聽$routeChangeError事件並將使用者重新導向 到登入頁上。由於事件是在$rootScope層級上,最好在run函數中綁定事件處理器。
app.run(["$rootScope", "$location", function($rootScope, $location) { $rootScope.$on("$routeChangeSuccess", function(userInfo) { console.log(userInfo); }); $rootScope.$on("$routeChangeError", function(event, current, previous, eventObj) { if (eventObj.authenticated === false) { $location.path("/login"); } });}]);
處理頁面重新整理
當使用者重新整理頁面時,服務將會失去現有狀態。我們需要從瀏覽器的session storage中擷取資料並將這些值賦值給loggerInUser變數。由於一個factory只會被調用一次,我們需要在一個初始化函數中設定這個變數,代碼如下所示:
function init() { if ($window.sessionStorage["userInfo"]) { userInfo = JSON.parse($window.sessionStorage["userInfo"]); } }init();
退出
當使用者想要從應用中退出時,相應的API必須連同包含在要求標頭部中的token一起被調用。一旦使用者退出,我們還需要清空sessionstorage中的資料。下面例子包含了一個退出函數,這個函數需要被添加到認證服務中。
function logout() { var deferred = $q.defer(); $http({ method: "POST", url: logoutUrl, headers: { "access_token": userInfo.accessToken } }).then(function(result) { $window.sessionStorage["userInfo"] = null; userInfo = null; deferred.resolve(result); }, function(error) { deferred.reject(error); }); return deferred.promise;}
總結
單頁應用的認證方式和傳統web應用的認證方式非常不同。由於主要的工作都搬到了瀏覽器端,使用者的狀態也需要儲存在用戶端。重要的一點是要記住使用者的狀態也需要的伺服器端儲存和進行驗證,因為駭客很可能慧聰用戶端竊取使用者的資料。
本文譯自Implementing Authentication in Angular Applications,原文地址http://www.sitepoint.com/implementing-authentication-angular-applications/
在AngularJS應用中實現認證授權