角色SPA具有多种角色

时间:2017-04-28 01:59:42

标签: angularjs token frontend

我正在使用Angular 1.5构建SPA,其中用户有多个角色[admin,'查看所有内容','编辑特定内容',基本用户]。我花了一整天的谷歌搜索,绊倒了大量不同的解决方案,大多数似乎已经过时了。我的后端是Slim应用程序,使用令牌认证。

我的问题:如何为每个角色实现不同的显示相同的ng-view?我登录后应该后端返回我的角色和令牌吗?如何在用户尝试更改位置时检查用户是否已登录?我假设只是检查令牌是否存储在客户端上是不够的,因为我无法知道它何时过期?

我是基于令牌的身份验证和Angular的新手。我不知道在我的情况下应如何处理前端,我不知道从哪里开始。我很感激任何答案,建议,提示,这将帮助我理解应该做什么以及如何做。谢谢。

2 个答案:

答案 0 :(得分:2)

首先,使用UI-Router管理您的状态,即网址。

登录机制

成功登录后,您的后端必须返回访问令牌,但可能会或可能不会返回角色。但是,您始终可以发送另一个请求来获取用户数据和角色,并将其保存到本地存储中。您可以创建一个服务,例如LoggedInUser获取用户信息和角色。

angular
  .module('myApp')
  .factory('LoggedInUser', [
    '$q',
    'AppConfig',
    'User',
    function ($q,
              AppConfig,
              User) {

      function getUser() {
        var defer = $q.defer();
        var userId = localStorage.getItem('$MyApp$userId');
        var user = null;

        var userStringified = localStorage.getItem('$MyApp$currentUser');
        try {
          user = JSON.parse(userStringified);
        } catch (error) {
          defer.reject(error);
        }

        if (user) {
          defer.resolve(user);
        } else {
          User.findById({ // This is a server query
            id: userId,
            filter: {
              include: 'roles'
            }
          })
            .$promise
            .then(function (userData) {
              localStorage.setItem('$MyApp$currentUser', JSON.stringify(userData));
              defer.resolve(userData);
            }, function (error) {
              defer.reject(error);
            });
        }

        return defer.promise;
      }

      function clearUser() {
          localStorage.removeItem('$MyApp$currentUser');
      }

      return {
        getUser: getUser,
        clearUser: clearUser
      }

    }]);

令牌到期

您可以使用Angular Interceptors拦截具有特定状态代码的服务器响应或您可以从服务器代码添加的任何自定义字段,例如code: 'TOKEN_EXPIRED'在响应对象上,然后执行一些操作,例如注销用户或从拦截器发送另一个请求以获取另一个令牌以继续操作。

这取决于您的请求的重要性。在我的情况下,我可以显示一条消息,注销用户。

多个角色

处理多个角色对前端和后端有不同的含义。

1。前端

在前端,限制分为两个级别

1.1。至整个视图

您可能希望根据某些条件阻止状态处于活动状态,例如

  • 忘记密码页面只应对未登录的用户可见,如果登录的用户尝试通过键入url来访问,则应重定向到他们的主页或仪表板页面。
  • 仪表板页面对于具有任何角色的登录用户应该是可见的。
  • 列表用户页面对于具有admin。角色的登录用户应该是可见的。

为了实现此限制,您将需要2个数据项,一个是检查状态是否需要身份验证,另一个是检查该状态的允许角色。我使用authenticate true/falseallowedRoles拥有一系列允许的角色。然后,您可以在$stateChangeStart事件中使用它们。这是代码:

在一些配置状态的js文件中:

$stateProvider
    .state('home', {
      abstract: true,
      url: '',
      templateUrl: 'app/home/home.html',
      controller: 'HomeController',
      resolve: {
        loggedInUser: [
          'LoggedInUser',
          '$window',
          '$q',
          'User',
          function (LoggedInUser,
                    $window,
                    $q,
                    User) {
            var defer = $q.defer();
            LoggedInUser.getUser()
              .then(function (user) {
                defer.resolve(user);
              }, function (error) {
                $window.alert('User is not authenticated.');
                defer.reject(error);
              });

            return defer.promise;
          }
        ]
      },
      data: {
        authenticate: true
      }
    })
    .state('home.dashboard', {
      url: '/dashboard',
      templateUrl: 'app/dashboard/dashboard.html',
      controller: 'DashboardController',
      data: {
        roles: ['admin', 'basic-user']
      }
    })
   .state('home.list-users', {
      url: '/list-users',
      templateUrl: 'app/users/list/list-users.html',
      controller: 'ListUsersController',
      data: {
        roles: ['admin']
      }
    })
    // More states

app.jsrun阻止

$rootScope.$on('$stateChangeStart', function (event, next, nextParams) {
    var userLoggedIn = User.isAuthenticated();
    /* If next state needs authentication and user is not logged in
     * then redirect to login page.
     */
    var authenticationRequired = (next.data && next.data.authenticate);
    if (authenticationRequired) {
      if (userLoggedIn) {
       // Check role of logged in user and allowed roles of state to see if state is protected
        var allowedRoles = next.data && next.data.roles;
        if (allowedRoles && allowedRoles.length > 0) {
          console.log('State access allowed to: ' + allowedRoles);
          LoggedInUser.getUser()
            .then(function (result) {
              var allowed = false;
              var user = result;
              var role = user.role;
              var allowed = (allowedRoles.indexOf(role) >= 0);
              console.log(role + ' can access ' + allowed);

              if (!allowed) {
                var isAdministrator = user.isAdministrator;
                if (isAdministrator) {
                  console.log('User is administrator: ' + isAdministrator);
                  role = 'administrator';
                  allowed = (allowedRoles.indexOf(role) >= 0);
                  console.log(role + ' can access ' + allowed);
                }
              }

              if (!allowed) {
                event.preventDefault(); //prevent current page from loading
                $state.go('home.dashboard');
                AppModalBox.show('danger', 'Not Authorized', 'You are not authorized to view this page.');
              }
            }, function (error) {
              console.log(error);
            });
        }
      } else {
        event.preventDefault(); // Prevent current page from loading
        $state.go('publicHome.auth.login');
      }
    } else {
      /* This code block handles publicly accessible page like login, forgot password etc.
       * Public states (pages) have redirectToLoggedInState data set to either true or false.
       * redirectToLoggedInState set to true/false means that public page cannot/can be accessed by the logged in user.
       *
       * If user is logged in and any public state having redirectToLoggedInState data set to true
       * then redirect to dashboard page and prevent opening page.
       */
      var redirectToLoggedInState = (next.data && next.data.redirectToLoggedInState);
      if (userLoggedIn && redirectToLoggedInState) {
        event.preventDefault(); //prevent current page from loading
        $state.go('home.dashboard');
      }
    }
  });
  

在超级状态下定义的数据在子状态上可用,因此您可以将所有私有状态(需要身份验证)分组到一个状态(在上面的示例中,它是home)并放置data.authenticate在超级大多数州。

1.2。视图中的某些部分

要获得此限制,您可以在控制器中使用相同的LoggedInUser服务,或使用最超级状态loggedInUser中的解析。将角色附加到$scope并使用ng-if根据角色显示和隐藏部分。

  

在所有子状态中都可以访问在最多状态上定义的结果。

以下是代码:

angular.module('myApp')
  .controller('DashboardController', [
    '$scope',
    'loggedInUser',
    function ($scope,
              loggedInUser) {

      $scope.roles = loggedInUser.roles;

    }]);

2。后端

您可以从localStorage获取访问令牌,您必须在成功登录后保存该令牌,并在每个请求中传递它。在后端,解析请求以检索访问令牌,获取此访问令牌的用户ID,获取用户的角色,并检查是否允许具有这些角色的用户执行操作(如查询)。

我正在使用NodeJS - ExpressJS,其中使用中间件来处理这种基于角色的身份验证。

答案 1 :(得分:0)

请检查

http://arthur.gonigberg.com/2013/06/29/angularjs-role-based-auth/

使用resolve来在路径中检查任何自定义函数

$ routeProvider.when('/ app',{     templateUrl:'app.html',     控制器:'AppController'     解决:{       // ...     }   });

你可以在这里找到使用resolve的例子

http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx