在Angular应用程序中实现身份验证

身份验证和授权是几乎每个严肃应用程序中的重要部分。 单页应用程序(SPA)也不例外。 该应用程序可能不会仅向任何用户公开其所有数据和功能。 用户可能必须进行身份验证才能查看应用程序的某些部分,或对应用程序执行某些操作。 要在应用程序中标识用户,我们需要使用户登录。

在传统的服务器驱动的应用程序和单页应用程序中,实现用户管理的方式有所不同。 SPA与其服务器组件进行交互的唯一方法是通过AJAX。 即使登录和注销也是如此。

负责识别用户的服务器必须公开身份验证端点。 SPA会将用户输入的凭据发送到此端点以进行验证。 在典型的基于令牌的身份验证系统中,服务可以在验证凭据后以访问令牌或包含已登录用户的名称和角色的对象进行响应。 客户端必须在对服务器发出的所有安全API请求中使用此访问令牌。

由于访问令牌将被多次使用,因此最好将其存储在客户端。 在Angular中,我们可以将值存储在服务或值中,因为它们是客户端上的单例对象。 但是,如果用户刷新页面,则服务或值中的值将丢失。 在这种情况下,最好使用浏览器提供的一种持久性机制来存储令牌。 最好是sessionStorage ,因为一旦关闭浏览器便将其清除。

实施登录

现在让我们看一些代码。 假设我们已经实现了所有服务器端逻辑,并且该服务在/api/login公开了一个REST端点,以检查登录凭据并返回访问令牌。 让我们编写一个简单的服务,通过点击身份验证端点来执行登录操作。 稍后我们将为该服务添加更多功能:

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

在实际的代码中,您可能希望将存储数据到sessionStorage的语句重构为一个单独的服务,因为如果我们这样做,该服务将承担多个责任。 我将其保留在同一服务中,以使演示保持简单。 该服务可以由处理应用程序登录功能的控制器使用。

固定路线

我们的应用程序中可能有一组安全的路由。 如果用户未登录并尝试输入这些路由之一,则应将用户定向到登录页面。 这可以通过使用路由选项中的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不是由框架定义的。 我定义了。 您可以根据用例将名称更改为任何名称。

通过或拒绝路线可能有多种原因。 根据方案,您可以在解决/拒绝承诺时传递对象。 我们尚未在服务中实现getLoggedInUser()方法。 这是一个简单的方法,可以从服务中返回loggedInUser对象。

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

  function getUserInfo() {
    return userInfo;
  }
});

通过上述片段中的promise发送的对象通过$rootScope广播。 如果路由已解析,则广播$routeChangeSuccess事件。 但是,如果路由失败,则会广播事件$routeChangeError 。 我们可以监听$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");
    }
  });
}]);

处理页面刷新

当用户在页面上单击刷新时,服务将失去其状态。 我们必须从浏览器的会话存储中获取数据,并将其分配给变量loggedInUser 。 由于工厂仅被调用一次,因此我们可以在初始化函数中设置此变量,如下所示。

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

init();

注销

当用户注销应用程序时,必须使用请求标头中包含的访问令牌来调用相应的API。 一旦用户注销,我们也应该清除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应用程序完全不同。 由于大部分工作是在客户端进行的,因此用户状态也必须存储在客户端的某个位置。 重要的是要记住,状态也应在服务器上维护和验证,因为黑客可能会窃取存储在客户端系统上的数据。

本文的源代码可在GitHub上下载。

From: https://www.sitepoint.com/implementing-authentication-angular-applications/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值