在AngularJS应用中实现认证授权

在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/

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏农夫安全

Bwapp漏洞平台答案全解-A3(第一篇)

A3 - Cross-Site Scripting (XSS) *****************跨站脚本****************** 跨站的终极思路...

6657
来自专栏张戈的专栏

借助PageSpeed,为Nginx网站服务器提速

网站加载速度越快,访客互动性、留住率和转换率就越高,这早已不是什么秘密。网站每延迟 100 毫秒,亚马逊的销售额就会减少 1%;延迟增加 500 毫秒,这意味着...

4047
来自专栏无所事事者爱嘲笑

ionic2 处理android硬件返回按钮

1374
来自专栏Golang语言社区

golang使用protobuf

为什么要使用protobuf 最近的项目中,一直使用Json做数据传输。Json用起来的确很方便。但相对于protobuf数据量更大些。做一个移动端应用,为用户...

8065
来自专栏破晓之歌

火狐浏览器各版本下载及插件httprequest

全历史版本下载: http://ftp.mozilla.org/pub/mozilla.org//firefox/releases/

1231
来自专栏前端儿

iPhone页面的常用调试方法

某些页面需要设置HOST才能进行访问,在iPhone上不好设置HOST,所以需要一些代理工具帮助我们

4811
来自专栏建站达人秀

如何搭建 Express 网站

Express 是一种保持最低程度规模的灵活 Node.js Web 应用程序框架,为 Web 和移动应用程序提供一组强大的功能。nenggou 使用您提议的各...

4992
来自专栏禅林阆苑

weex 踩坑笔记 【原创】

weex 踩坑笔记 Write By CS逍遥剑仙 我的主页: www.csxiaoyao.com GitHub: github.com/cs...

51110
来自专栏全栈之路

vue.js安装心得

npm是nodejs的包管理器,所以之前只想找npm,而不想下载nodejs,最后闹了笑话。

1632
来自专栏yukong的小专栏

【SpringBoot系列01】初识SpringBoot

【SpringBoot系列02】SpringBoot之使用Thymeleaf视图模板

1575

扫码关注云+社区

领取腾讯云代金券