如何在AngularJS控制器中指定事件优先级?

时间:2016-09-09 07:27:50

标签: angularjs

我有一个带有用户识别功能的Spring + AngularJS RestFul应用程序。

用户登录后,我会按以下方式存储此数据:

$window.sessionStorage.user

因为我希望在我的主控制器中实现的其他选项卡中有sessionStorage这里提出的解决方案:https://stackoverflow.com/a/32766809/1281500

此外,在此控制器中,我会收听$stateChangeStart个事件以检查用户是否已登录。如果用户未登录,我会将他重定向到相应的登录页面。

当我在资源管理器中打开一个新标签时出现问题。正如您在下面的代码中看到的那样,我从旧标签页获取$window.sessionStorage.user变量到新标签页,如下所示:

else if (event.key == 'sessionStorage' && !$window.sessionStorage.length) {
        // another tab sent data <- get it
        var data = JSON.parse(event.newValue);
        for (var key in data) {
            $window.sessionStorage.setItem(key, data[key]);
        }

但是$stateChangeStart的代码是在上面的代码段之前执行的,因此$window.sessionStorage.user尚未可用,并且即使用户已经登录,用户也总是被重定向到登录页面(至少在原始标签)

$rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
        // if the user tries to access a restricted page an is not logged in, he is redirected to the login view
        var restrictedPage = $.inArray(toState.name, ['login', 'sign-up']) === -1;
        // "$window.sessionStorage.user" IS ALWAYS "UNDEFINED" just after a new tab is opened
        if (restrictedPage && $window.sessionStorage.user === undefined) {
            // Go to login page
        }
    });

完整的控制器代码如下。如何在$stateChangeStart块中提供用户凭据?

(function() {
  'use strict';

  angular
    .module('app.core')
    .controller('CoreController', CoreController);

  CoreController.$inject = ['$q', '$scope', '$rootScope', '$state', '$location', '$cookies', 'LoginService', '$window'];

  function CoreController($q, $scope, $rootScope, $state, $location, $cookies, LoginService, $window) {
    var vm = this;

    var sessionStorage_transfer = function(event) {
      if(!event) { event = $window.event; } // ie suq
      if(!event.newValue) return;          // do nothing if no value to work with
      if (event.key == 'getSessionStorage') {
        // another tab asked for the sessionStorage -> send it
        $window.localStorage.setItem('sessionStorage', JSON.stringify(sessionStorage));
        // the other tab should now have it, so we're done with it.
        $window.localStorage.removeItem('sessionStorage'); // <- could do short timeout as well.
      } else if (event.key == 'sessionStorage' && !$window.sessionStorage.length) {
        // another tab sent data <- get it
        var data = JSON.parse(event.newValue);
        for (var key in data) {
            $window.sessionStorage.setItem(key, data[key]);
        }
      }
    };

    // listen for changes to localStorage
    if($window.addEventListener) {
        $window.addEventListener("storage", sessionStorage_transfer);
    } else {
        $window.attachEvent("onstorage", sessionStorage_transfer);
    };

    // ask other tabs for session storage (this is ONLY to trigger event)
    if (!$window.sessionStorage.length) {
        $window.localStorage.setItem('getSessionStorage', 'user');
        $window.localStorage.removeItem('getSessionStorage', 'user');
    };

    $rootScope.$on('$stateChangeStart', function (event, toState, toParams) {
        // if the user tries to access a restricted page an is not logged in, he is redirected to the login view
        var restrictedPage = $.inArray(toState.name, ['login', 'sign-up']) === -1;
        if (restrictedPage && $window.sessionStorage.user === undefined) {
            // Go to login page
        }
    });

  }
})();

更新

我的应用程序是无状态,因此它按以下方式工作:

  • 用户第一次访问应用程序时,我检查用户名和密码是否正确(调用Spring Rest服务),然后我为用户生成一个令牌。此令牌和用户数据都存储在session $window.sessionStorage.authToken$window.sessionStorage.authToken之类的会话存储中。 我从另一个AngularJS控制器(LoginController)管理它:
function login(valid) {
        if(!valid) return;

        LoginService.authenticate(vm.user)
        .then(
                function(user) {
                    var authToken = user.token;
                    $window.sessionStorage.authToken = authToken;
                    vm.authError = false;
                    if (vm.rememberMe) {
                        var now = new Date();
                        var expireDate = new Date(now.getFullYear(), now.getMonth() + 1, now.getDate());
                        $cookies.put(AUTH_TOKEN_COOKIE, authToken, {'expires': expireDate});
                    }
                    LoginService.getUser()
                    .then(
                        function(user) {
                            $window.sessionStorage.user = user
                            $state.go('installations');
                        }
                    );
                },
                function(errAuthentication) {
                    vm.authError = true;
                    $window.sessionStorage.user = null;
                }
        );
    }
  • 从现在开始,我发送此令牌以及每个请求到RESTful应用程序,我在Spring中有一个过滤器,用户凭证检查令牌是否正确。如果此令牌不存在或不再有效,Spring Security会抛出相应的Exception,我会捕获此异常以重定向到登录页面(我不认为此代码对当前问题很重要,但我可以发布它如有必要)。

所以基本上我的用户只存在于当前的sessionStorage对象中,我没有检查用户是否登录的服务,我只检查我在第一次登录过程中存储的变量

我希望这有助于澄清我的过程。

非常感谢。

1 个答案:

答案 0 :(得分:0)

确保您的控制器在其他模块之前运行真​​的很难。

如果您想在run module上执行此操作之前执行此检查,请执行此操作。

angular
.module('myApp')
.run(runBlock)
.run(['mySessionChecker', '$rootScope', function (mySessionChecker, $rootScope ) {

  $rootScope.$on("$locationChangeStart", function () {
      //Service that checks if the user is logged or not
      mySessionChecker.checkSession().then( function (auth) {
       //Auth tells you if the user is logged in or not
       console.log('User is '+auth);
       doSomething();
      })
  });

}]);

function runBlock($log) {
    $log.log('Angular's running');
}

Run Module将在App Module之后立即启动。

然后,例如,您可以emit an event使用$rootScope或使用Postal.js并对该信息执行某些操作。

我会创建一个Parent Controller来拦截event,我会扩展您需要进行检查的控制器,以了解用户是否已登录。

看看at this Q/A

希望我能提供帮助。