AngularJS ui-router 登录验证

378
我对AngularJS比较新,有一些疑惑如何在以下场景中使用angular-"ui-router":
我正在构建一个Web应用程序,该应用程序由两个部分组成。第一部分是主页及其登录和注册视图,第二部分是仪表板(成功登录后)。
我已经创建了一个index.html文件来处理主页部分的Angular应用程序和ui-router配置以处理/login/signup视图,并且还有另一个dashboard.html文件,用于仪表板部分的应用程序和ui-router配置来处理许多子视图。
现在,我已经完成了仪表板部分,但不知道如何将这两个不同的Angular应用程序结合起来。我该如何告诉主页应用程序重定向到仪表板应用程序?

1
你能和我们分享一些代码吗? - Chancho
6
@Chancho,我认为这不是关于代码的问题,实际上我不知道应该分享哪些代码。 - Ahmed Hashem
是的,请分享代码,问题非常泛泛。 - Alireza
10个回答

610

我正在制作一个更漂亮的演示,并将这些服务清理成可使用的模块,但这是一个复杂的过程需要解决一些限制条件,请耐心等待。你需要将其分解成几个部分。

查看这个 plunk

首先,你需要一个用于存储用户身份的服务。我称它为 principal。可以检查它以查看用户是否已登录,并在请求时解析代表用户身份基本信息的对象。这可以是你需要的任何内容,但必要的内容包括显示名称、用户名、可能是电子邮件和用户所属的角色(如果适用于你的应用程序)。Principal 还具有执行角色检查的方法。

.factory('principal', ['$q', '$http', '$timeout',
  function($q, $http, $timeout) {
    var _identity = undefined,
      _authenticated = false;

    return {
      isIdentityResolved: function() {
        return angular.isDefined(_identity);
      },
      isAuthenticated: function() {
        return _authenticated;
      },
      isInRole: function(role) {
        if (!_authenticated || !_identity.roles) return false;

        return _identity.roles.indexOf(role) != -1;
      },
      isInAnyRole: function(roles) {
        if (!_authenticated || !_identity.roles) return false;

        for (var i = 0; i < roles.length; i++) {
          if (this.isInRole(roles[i])) return true;
        }

        return false;
      },
      authenticate: function(identity) {
        _identity = identity;
        _authenticated = identity != null;
      },
      identity: function(force) {
        var deferred = $q.defer();

        if (force === true) _identity = undefined;

        // check and see if we have retrieved the 
        // identity data from the server. if we have, 
        // reuse it by immediately resolving
        if (angular.isDefined(_identity)) {
          deferred.resolve(_identity);

          return deferred.promise;
        }

        // otherwise, retrieve the identity data from the
        // server, update the identity object, and then 
        // resolve.
        //           $http.get('/svc/account/identity', 
        //                     { ignoreErrors: true })
        //                .success(function(data) {
        //                    _identity = data;
        //                    _authenticated = true;
        //                    deferred.resolve(_identity);
        //                })
        //                .error(function () {
        //                    _identity = null;
        //                    _authenticated = false;
        //                    deferred.resolve(_identity);
        //                });

        // for the sake of the demo, fake the lookup
        // by using a timeout to create a valid
        // fake identity. in reality,  you'll want 
        // something more like the $http request
        // commented out above. in this example, we fake 
        // looking up to find the user is
        // not logged in
        var self = this;
        $timeout(function() {
          self.authenticate(null);
          deferred.resolve(_identity);
        }, 1000);

        return deferred.promise;
      }
    };
  }
])

其次,您需要一个服务来检查用户想要访问的状态,确保他们已登录(如果必要;对于登录、密码重置等操作不需要),然后进行角色检查(如果您的应用需要此功能)。如果他们未经过身份验证,请将他们发送到登录页面。如果他们已通过身份验证但未通过角色检查,请将他们发送到拒绝访问页面。我将这个服务称为授权

.factory('authorization', ['$rootScope', '$state', 'principal',
  function($rootScope, $state, principal) {
    return {
      authorize: function() {
        return principal.identity()
          .then(function() {
            var isAuthenticated = principal.isAuthenticated();

            if ($rootScope.toState.data.roles
                && $rootScope.toState
                             .data.roles.length > 0 
                && !principal.isInAnyRole(
                   $rootScope.toState.data.roles))
            {
              if (isAuthenticated) {
                  // user is signed in but not
                  // authorized for desired state
                  $state.go('accessdenied');
              } else {
                // user is not authenticated. Stow
                // the state they wanted before you
                // send them to the sign-in state, so
                // you can return them when you're done
                $rootScope.returnToState
                    = $rootScope.toState;
                $rootScope.returnToStateParams
                    = $rootScope.toStateParams;

                // now, send them to the signin state
                // so they can log in
                $state.go('signin');
              }
            }
          });
      }
    };
  }
])
现在你需要做的就是监听ui-router$stateChangeStart事件。这将给你一个机会来检查当前状态、他们想要去到的状态,并插入你的授权检查。如果检查失败,你可以取消路由转换或切换到不同的路线。
.run(['$rootScope', '$state', '$stateParams', 
      'authorization', 'principal',
    function($rootScope, $state, $stateParams, 
             authorization, principal)
{
      $rootScope.$on('$stateChangeStart', 
          function(event, toState, toStateParams)
      {
        // track the state the user wants to go to; 
        // authorization service needs this
        $rootScope.toState = toState;
        $rootScope.toStateParams = toStateParams;
        // if the principal is resolved, do an 
        // authorization check immediately. otherwise,
        // it'll be done when the state it resolved.
        if (principal.isIdentityResolved()) 
            authorization.authorize();
      });
    }
  ]);

在追踪用户身份时,棘手的部分是如果已经进行了身份验证(例如,在上一个会话后访问页面并在cookie中保存了授权令牌,或者您刷新了页面或从链接跳转到了URL),则需要查找身份。由于ui-router的工作方式,您需要在进行身份验证之前先进行身份解析。您可以使用状态配置中的resolve选项来完成此操作。我为站点设置了一个父状态,所有状态都继承自该状态,这强制解析主体先于其他任何事件发生。

$stateProvider.state('site', {
  'abstract': true,
  resolve: {
    authorize: ['authorization',
      function(authorization) {
        return authorization.authorize();
      }
    ]
  },
  template: '<div ui-view />'
})

这里还有一个问题... resolve 只被调用了一次。一旦您的身份查找承诺完成,它将不会再运行 resolve 委托。因此,我们必须在两个地方执行身份验证检查:一次是在resolve中的身份识别承诺解决期间,这涵盖了应用程序加载时的第一次,另一次是在 $stateChangeStart 中,如果已经解析,那么覆盖了任何时间您在状态之间导航。

好的,到目前为止我们做了什么?

  1. 检查应用程序在加载时用户是否已登录。
  2. 跟踪已登录用户的信息。
  3. 重定向到需要用户已登录的状态的登录状态。
  4. 如果没有权限访问,则将其重定向到访问被拒绝的状态。
  5. 如果需要他们登录,我们有一种机制可以将用户重定向回他们请求的原始状态。
  6. 我们可以注销用户(需要与管理您的授权票证的任何客户端或服务器代码一起使用)。
  7. 我们不需要每次重新加载浏览器或单击链接时都将用户发送回登录页面。

从这里开始怎么办呢?好吧,您可以将状态组织成需要登录的区域。通过向这些状态(或其父级,如果要使用继承)添加具有角色data,可以要求经过身份验证/授权的用户。在这里,我们将资源限制为管理员:

.state('restricted', {
    parent: 'site',
    url: '/restricted',
    data: {
      roles: ['Admin']
    },
    views: {
      'content@': {
        templateUrl: 'restricted.html'
      }
    }
  })
现在您可以逐个状态地控制用户可以访问的路由。还有其他问题吗?也许只要根据是否登录就能变化视图的一部分?没问题。使用principal.isAuthenticated()或者principal.isInRole(),以及诸多条件显示模板或元素的方法。
首先,在控制器中注入principal,并将其绑定到作用域上,这样就可以在视图中轻松使用它了:
.scope('HomeCtrl', ['$scope', 'principal', 
    function($scope, principal)
{
  $scope.principal = principal;
});

显示或隐藏一个元素:

<div ng-show="principal.isAuthenticated()">
   I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
  I'm not logged in
</div>

等等等等,总之,在您的示例应用程序中,您将拥有一个主页状态,该状态将允许未经身份验证的用户访问。他们可以链接到登录或注册状态,或者将这些表单构建到该页面中。无论什么适合您。

仪表板页面可以从要求用户已登录且为“用户”角色成员的状态继承。我们讨论过的所有授权内容都将从那里流向。


28
谢谢,这确实帮助我整理自己的代码。顺便说一句,如果出现无限路由循环(UI Router bug),请尝试使用 $location.path 而不是 $state.go - JacobF
2
这是一个很好的答案,对我帮助很大。当我在控制器中设置user = principal并尝试在我的视图中调用say user.identity().name以获取当前登录用户的名称时,我似乎只得到了promise对象{then: fn,catch: fn,finally:}而不是实际的_identity对象。如果我使用user.identity.then(fn(user)),我可以获得用户对象,但这似乎是视图中的大量代码,我是否遗漏了什么? - Mark
4
我会先在控制器中解析身份,并将其分配给$scope.user,然后在您的 then 函数中执行此操作。您仍然可以在视图中引用 user;当它被解析时,视图将被更新。 - moribvndvs
7
@jvannistelrooy 我也遇到了go()的问题,但是在调用一个noop函数后放入then里面之后,像这样$q.when(angular.noop).then(function(){$state.go('myState')}),一切都顺利进行。如果我在另一个状态过渡未完成时调用$state.go,它将无法正常工作(我认为这就是它无法正常工作的原因)。 - Sebastian
2
@Andi 如果您想强制重新解析标识(从而使其发出Web请求以检查您与远程服务器的身份验证或其他机制),而不是继续使用在客户端缓存的标识,请这样做。例如,如果您向某个资源发出请求并收到401错误,则可能希望清除缓存的标识,以确保用户的声明仍然有效,然后再允许他们执行或访问受保护的资源。或者,您可能会定期执行此操作,以确保用户实际上仍然已登录并已授权。 - moribvndvs
显示剩余24条评论

120
迄今为止发布的解决方案在我看来过于复杂。有一个更简单的方法,ui-router文档中提到了监听$locationChangeSuccess并使用$urlRouter.sync()来检查状态转换、停止它或恢复它。但即便如此,实际上这也不起作用。

然而,这里有两个简单的替代方案。选择其中之一:

解决方案1:监听$locationChangeSuccess

你可以监听$locationChangeSuccess,甚至可以在那里执行一些逻辑,包括异步逻辑。基于该逻辑,你可以让函数返回未定义,这将导致状态转换像正常一样继续进行,或者如果用户需要被认证,你可以执行$state.go('logInPage')。以下是一个例子:

angular.module('App', ['ui.router'])

// In the run phase of your Angular application  
.run(function($rootScope, user, $state) {

  // Listen to '$locationChangeSuccess', not '$stateChangeStart'
  $rootScope.$on('$locationChangeSuccess', function() {
    user
      .logIn()
      .catch(function() {
        // log-in promise failed. Redirect to log-in page.
        $state.go('logInPage')
      })
  })
})

请记住,这并不能真正防止目标状态的加载,但如果用户未经授权,则会重定向到登录页面。这没关系,因为真正的保护措施已经在服务器上了。

解决方案2:使用状态resolve

在这个解决方案中,您可以使用ui-router 解决功能

如果用户未经过身份验证,则在resolve中拒绝承诺并将其重定向到登录页面。

操作步骤如下:

angular.module('App', ['ui.router'])

.config(
  function($stateProvider) {
    $stateProvider
      .state('logInPage', {
        url: '/logInPage',
        templateUrl: 'sections/logInPage.html',
        controller: 'logInPageCtrl',
      })
      .state('myProtectedContent', {
        url: '/myProtectedContent',
        templateUrl: 'sections/myProtectedContent.html',
        controller: 'myProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })
      .state('alsoProtectedContent', {
        url: '/alsoProtectedContent',
        templateUrl: 'sections/alsoProtectedContent.html',
        controller: 'alsoProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })

    function authenticate($q, user, $state, $timeout) {
      if (user.isAuthenticated()) {
        // Resolve the promise successfully
        return $q.when()
      } else {
        // The next bit of code is asynchronously tricky.

        $timeout(function() {
          // This code runs after the authentication promise has been rejected.
          // Go to the log-in page
          $state.go('logInPage')
        })

        // Reject the authentication promise to prevent the state from loading
        return $q.reject()
      }
    }
  }
)

与第一种解决方案不同,这个解决方案实际上阻止了目标状态的加载。


6
@FredLackey说未经验证的用户处于“状态A”。他们单击链接以进入“受保护的状态B”,但您想将它们重定向到“登录页面”。如果没有$timeoutui-router将简单地停止所有状态转换,因此用户将卡在“状态A”中。$timeout允许ui-router首先阻止初始转换到“受保护的状态B”,因为解析被拒绝,之后再重定向到“登录页面”。 - M.K. Safi
@Imray authenticate 函数作为参数传递给了 ui-router。您不必自己调用它,ui-router 会调用它。 - M.K. Safi
你为什么要使用'$locationChangeSuccess'而不是'$stateChangeStart'呢? - Draex_
@PeterDraexDräxler 我主要是按照文档来的。你使用 $stateChangeStart 有注意到什么不同吗? - M.K. Safi
@MKSafi $locationChangeSuccess在新状态完全加载后触发。$stateChangeStart在加载新状态之前触发。因此,使用$locationChangeSuccess时,“禁止访问”页面会显示半秒钟,而使用$stateChangeStart则不会。 - Draex_
显示剩余8条评论

42

最简单的解决方法是使用$stateChangeStartevent.preventDefault() 来在用户未经过身份验证时取消状态更改,并将用户重定向到登录页面auth状态。

angular
  .module('myApp', [
    'ui.router',
  ])
    .run(['$rootScope', 'User', '$state',
    function ($rootScope, User, $state) {
      $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
        if (toState.name !== 'auth' && !User.authenticaded()) {
          event.preventDefault();
          $state.go('auth');
        }
      });
    }]
  );

6
如果User.authenticated()是一个异步调用,我不认为这会起作用。这就是每个人都在追求的圣杯。例如,如果除了“登录”状态之外的每个状态都受到保护,我想在加载任何状态之前确认用户仍然经过身份验证。使用解析很麻烦,因为它们只能解析一次,为了防止子状态加载,您必须将解析注入到*每个子状态中。 - Jason
在我的情况下,authenticated 不是一个异步调用:this.authenticated = function() { if (this.currentAccountID !== null) { return true; } return false; }; - sebest
根据:https://dev59.com/hZjga4cB1Zd3GeqPRO6f#38374313,'run' 明显优于 'service',因此引发了问题。检查本地存储的身份验证状态似乎是一个不错的方法。 - Deepak Thomas

22

我认为您需要一个处理身份验证流程(及其存储)的服务

在这个服务中,您将需要一些基本方法:

  • isAuthenticated()
  • login()
  • logout()
  • 等等……

这个服务应该被注入到每个模块的控制器中:

  • 在您的仪表板部分,使用此服务检查用户是否已通过身份验证 (service.isAuthenticated() 方法)。如果没有,重定向到 /login。
  • 在您的登录部分,只需使用表单数据通过您的 service.login() 方法对用户进行身份验证。

这种行为的一个很好且健壮的示例是angular-app 项目,特别是它的安全模块,它基于了与众不同的HTTP Auth 拦截器模块

希望这可以帮助您。


21

我创建了这个模块,以帮助使这个过程变得轻而易举。

你可以做以下事情:

$routeProvider
  .state('secret',
    {
      ...
      permissions: {
        only: ['admin', 'god']
      }
    });

或者也可以这样说

$routeProvider
  .state('userpanel',
    {
      ...
      permissions: {
        except: ['not-logged-in']
      }
    });

这是全新的,但值得一试!

https://github.com/Narzerus/angular-permission


2
有什么办法阻止我在运行时编辑源代码,删除你的“管理员”或“上帝”,然后继续操作呢? - Pogrindis
12
希望所有需要授权的数据请求在服务器上也得到了验证。 - Ben Ripley
24
这不是为了安全而设计的,客户端授权从来就不是安全的,因为你总可以改变值。甚至可以拦截服务器端的响应并将其评估为“已授权”。客户端权限/授权的目的在于避免让用户进行禁止的操作,以提高用户体验。例如,如果你正在处理仅限管理员的操作,即使用户恶意欺骗客户端允许向服务器发送受限请求,服务器仍然会返回 401 响应。当然,这始终是实施 API 的责任。@BenRipley 确实如此 - Rafael Vidaurre
3
对这个问题做出了很好的回应,Rafael。始终保护API,因为前端是最容易被反向工程和伪造的东西之一。 - Frankie Loscavio
1
这个历史记录问题已经解决了一段时间了@Bohdan。即使使用ui-router extras,您也可以放心使用它。 - masterspambot
显示剩余2条评论

16

我想分享另一种适用于ui router 1.0.0.X的解决方案。

正如您所知,stateChangeStart和stateChangeSuccess现已弃用。 https://github.com/angular-ui/ui-router/issues/2655

相反,您应该使用$transitions http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html

这是我实现它的方式:

首先,我有一个AuthService,其中包含一些有用的函数。

angular.module('myApp')

        .factory('AuthService',
                ['$http', '$cookies', '$rootScope',
                    function ($http, $cookies, $rootScope) {
                        var service = {};

                        // Authenticates throug a rest service
                        service.authenticate = function (username, password, callback) {

                            $http.post('api/login', {username: username, password: password})
                                    .success(function (response) {
                                        callback(response);
                                    });
                        };

                        // Creates a cookie and set the Authorization header
                        service.setCredentials = function (response) {
                            $rootScope.globals = response.token;

                            $http.defaults.headers.common['Authorization'] = 'Bearer ' + response.token;
                            $cookies.put('globals', $rootScope.globals);
                        };

                        // Checks if it's authenticated
                        service.isAuthenticated = function() {
                            return !($cookies.get('globals') === undefined);
                        };

                        // Clear credentials when logout
                        service.clearCredentials = function () {
                            $rootScope.globals = undefined;
                            $cookies.remove('globals');
                            $http.defaults.headers.common.Authorization = 'Bearer ';
                        };

                        return service;
                    }]);

然后我有这个配置:

angular.module('myApp', [
    'ui.router',
    'ngCookies'
])
        .config(['$stateProvider', '$urlRouterProvider',
            function ($stateProvider, $urlRouterProvider) {
                $urlRouterProvider.otherwise('/resumen');
                $stateProvider
                        .state("dashboard", {
                            url: "/dashboard",
                            templateUrl: "partials/dashboard.html",
                            controller: "dashCtrl",
                            data: {
                                authRequired: true
                            }
                        })
                        .state("login", {
                            url: "/login",
                            templateUrl: "partials/login.html",
                            controller: "loginController"
                        })
            }])

        .run(['$rootScope', '$transitions', '$state', '$cookies', '$http', 'AuthService',
            function ($rootScope, $transitions, $state, $cookies, $http, AuthService) {

                // keep user logged in after page refresh
                $rootScope.globals = $cookies.get('globals') || {};
                $http.defaults.headers.common['Authorization'] = 'Bearer ' + $rootScope.globals;

                $transitions.onStart({
                    to: function (state) {
                        return state.data != null && state.data.authRequired === true;
                    }
                }, function () {
                    if (!AuthService.isAuthenticated()) {
                        return $state.target("login");
                    }
                });
            }]);
你可以看到我使用了

data: {
   authRequired: true
}

标记仅在已验证身份的情况下才能访问该状态。

然后,在.run中,我使用转换来检查已验证的状态。

$transitions.onStart({
    to: function (state) {
        return state.data != null && state.data.authRequired === true;
    }
}, function () {
    if (!AuthService.isAuthenticated()) {
        return $state.target("login");
    }
});

使用$transitions文档中找到的一些代码构建了此示例。我对ui路由器还不太熟悉,但它可以正常工作。

希望它能帮助任何人。


这对于使用较新路由器的人来说非常棒。谢谢! - mtro

5

以下是我们如何解决无限路由循环并仍然使用$state.go而不是$location.path

if('401' !== toState.name) {
  if (principal.isIdentityResolved()) authorization.authorize();
}

1
有人知道为什么使用上述接受的答案/设置时,地址栏不再显示URL和所有片段和查询字符串参数吗?自从实施这个之后,地址栏不再允许我们的应用程序被加入书签。 - Frankie Loscavio
1
这不应该是对现有答案之一的评论吗?因为在 OP 中没有这样的代码,甚至不清楚这是指哪个答案/什么代码。 - T J

3
我有另一个解决方案:当您只想在登录时显示内容时,该解决方案可以完美地工作。定义一个规则,检查您是否已登录并且不是白名单路由的路径。
$urlRouterProvider.rule(function ($injector, $location) {
   var UserService = $injector.get('UserService');
   var path = $location.path(), normalized = path.toLowerCase();

   if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
     $location.path('/login/signin');
   }
});

在我的示例中,如果我未登录且当前要路由的路由不是“/login”的一部分,则会进行检查,因为我的白名单路由如下:

/login/signup // registering new user
/login/signin // login to app

因此,我可以立即访问这两个路由,如果您在线,则将检查其他所有路由。

这是我的整个登录模块的路由文件:

export default (
  $stateProvider,
  $locationProvider,
  $urlRouterProvider
) => {

  $stateProvider.state('login', {
    parent: 'app',
    url: '/login',
    abstract: true,
    template: '<ui-view></ui-view>'
  })

  $stateProvider.state('signin', {
    parent: 'login',
    url: '/signin',
    template: '<login-signin-directive></login-signin-directive>'
  });

  $stateProvider.state('lock', {
    parent: 'login',
    url: '/lock',
    template: '<login-lock-directive></login-lock-directive>'
  });

  $stateProvider.state('signup', {
    parent: 'login',
    url: '/signup',
    template: '<login-signup-directive></login-signup-directive>'
  });

  $urlRouterProvider.rule(function ($injector, $location) {
    var UserService = $injector.get('UserService');
    var path = $location.path();

    if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
         $location.path('/login/signin');
    }
  });

  $urlRouterProvider.otherwise('/error/not-found');
}

() => { /* code */ }是ES6语法,应该使用function() { /* code */ }


3

使用$http拦截器

通过使用$http拦截器,您可以向后端发送头信息或者反过来,并以此进行检查。

关于$http拦截器的优秀文章:$http拦截器

示例:

$httpProvider.interceptors.push(function ($q) {
        return {
            'response': function (response) {

                // TODO Create check for user authentication. With every request send "headers" or do some other check
                return response;
            },
            'responseError': function (reject) {

                // Forbidden
                if(reject.status == 403) {
                    console.log('This page is forbidden.');
                    window.location = '/';
                // Unauthorized
                } else if(reject.status == 401) {
                    console.log("You're not authorized to view this page.");
                    window.location = '/';
                }

                return $q.reject(reject);
            }
        };
    });

将此内容放在您的.config或.run函数中。

2

首先,您需要一个可以注入到控制器中的服务,该服务具有应用程序身份验证状态的一些想法。使用本地存储持久化身份验证详细信息是一个不错的方法。

接下来,在状态更改之前检查身份验证状态。由于您的应用程序有一些需要进行身份验证的页面和其他不需要进行身份验证的页面,因此创建一个检查身份验证的父路由,并使所有其他需要相同的页面成为该父级的子级。

最后,您需要某种方式来确定当前登录的用户是否能执行某些操作。这可以通过向您的身份验证服务添加“can”函数来实现。Can接受两个参数: - action - 必需 - (例如“manage_dashboards”或“create_new_dashboard”) - object - 可选 - 正在操作的对象。例如,如果您有一个仪表板对象,则可能要检查仪表板.ownerId === loggedInUser.id。(当然,永远不应信任从客户端传递的信息,并且在将其写入数据库之前,您应始终在服务器上进行验证)。

angular.module('myApp', ['ngStorage']).config([
   '$stateProvider',
function(
   $stateProvider
) {
   $stateProvider
     .state('home', {...}) //not authed
     .state('sign-up', {...}) //not authed
     .state('login', {...}) //not authed
     .state('authed', {...}) //authed, make all authed states children
     .state('authed.dashboard', {...})
}])
.service('context', [
   '$localStorage',
function(
   $localStorage
) {
   var _user = $localStorage.get('user');
   return {
      getUser: function() {
         return _user;
      },
      authed: function() {
         return (_user !== null);
      },
      // server should return some kind of token so the app 
      // can continue to load authenticated content without having to
      // re-authenticate each time
      login: function() {
         return $http.post('/login.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      // this request should expire that token, rendering it useless
      // for requests outside of this session
      logout: function() {
         return $http.post('logout.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      can: function(action, object) {
         if (!this.authed()) {
            return false;
         }

         var user = this.getUser();

         if (user && user.type === 'admin') {
             return true;
         }

         switch(action) {
            case 'manage_dashboards':
               return (user.type === 'manager');
         }

         return false;


      }
   }
}])
.controller('AuthCtrl', [
   'context', 
   '$scope', 
function(
   context, 
   $scope
) {
   $scope.$root.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      //only require auth if we're moving to another authed page
      if (toState && toState.name.indexOf('authed') > -1) {
         requireAuth();
      }
   });

   function requireAuth() {
      if (!context.authed()) {
         $state.go('login');
      }
   }
}]

** 免责声明:以上代码为伪代码,不提供任何保证 **


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接