打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
Implementing Authentication in Angular Applications

Authentication and authorization are important pieces on almost every serious application. Single Page Applications (SPAs) are no exception. The application may not expose all of its data and functionality to just any user. Users may have to authenticate themselves to see certain portions of the application, or to perform certain action on the application. To identify the user in the application, we need get the user logged in.

There is a difference in the way user management is implemented in traditional server-driven applications and Single Page Applications. The only way in which an SPA can interact with its server components is through AJAX. This is true even for logging in and logging out.

The server responsible for identifying the user has to expose an authentication endpoint. The SPA will send the credentials entered by the user to this endpoint to for verification. In a typical token based authentication system, the service may respond with an access token or with an object containing the name and role of the logged in user after validating the credentials. The client has to use this access token in all secured API requests made to the server.

As the access token will be used multiple times, it is better to store it on the client side. In Angular, we can store the value in a service or a value as they are singleton objects on the client side. But, if the user refreshes the page, the value in the service or value would be lost. In such case, it is better to store the token using one of the persistence mechanisms offered by the browser; preferably sessionStorage, as it is cleared once the browser is closed.

Implementing Login

Let’s have a look at some code now. Assume that we have all server side logic implemented and the service exposes a REST endpoint at /api/login to check login credentials and return an access token. Let’s write a simple service that performs the login action by hitting the authentication endpoint. We will add more functionality to this service later:

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  };});

In actual code, you may want to re-factor the statement storing data to sessionStorage into a separate service, as this service gets multiple responsibilities if we do this. I am leaving it in the same service to keep the demo simple. This service can be consumed by a controller that handles the login functionality for the application.

Securing Routes

We may have a set of secured routes in the application. If a user is not logged in and attempts to enter one of those routes, the user should be directed to the login page. This can be achieved using the resolve block in the routing options. The following snippet gives an idea on the implementation:

$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 });      }    }]  }});

The resolve block can contain multiple blocks of statements that have to return promise objects on completion. Just to clarify, the name auth defined above is not defined by the framework; I defined it. You can change the name to anything based on the use case.

There can be multiple reasons to pass or reject the route. Based on the scenario, you can pass an object while resolving/rejecting the promise. We haven’t implemented the getLoggedInUser() method yet in the service. It is a simple method that returns the loggedInUser object from the service.

app.factory("authenticationSvc", function() {  var userInfo;  function getUserInfo() {    return userInfo;  }});

The objects sent via the promise in the above snippet are broadcasted via $rootScope. If the route is resolved, the event $routeChangeSuccess is broadcast. However, if the route is failed, the event $routeChangeError is broadcast. We can listen to the $routeChangeError event and redirect the user to the login page. As the event is at $rootScope level, it is better to attach handlers to the event in a run block.

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");    }  });}]);

Handling Page Refresh

When a user hits refresh on a page, the service loses its state. We have to get the data from the browser’s session storage and assign it to the variable loggedInUser. As a factory is invoked only once, we can set this variable in an initialization function, as shown below.

function init() {  if ($window.sessionStorage["userInfo"]) {    userInfo = JSON.parse($window.sessionStorage["userInfo"]);  }}init();

Logging Out

When a user logs out of the application, the corresponding API has to be invoked with the access token included in the request headers. Once the user is logged out, we should clear the data in sessionStorage as well. The following example contains the logout function that has to be added to the authentication service.

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;}

Conclusion

The approach for implementing authentication in Single Page Applications is quite different from that of traditional web applications. As the majority of the work is carried out on the client side, the state of the user also has to be stored somewhere in the client. It is important to remember that the state should be maintained and validated on the server as well, as a hacker can potentially steal the data stored on the client system.

The source code from this article is available for download on GitHub.

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
Angular.JS 与node.JS架构中基于token身份验证实现。
使用json web token | 大前端
通过一个实际案例彻底理解 promise
php实现新浪、腾讯、淘宝登陆的代码
httprunner 2.x学习5-测试用例集(testsuite)
国产优秀node.js框架thinkjs教程之三
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服