I have a Spring+AngularJS RestFul application with user identification capabilites.
As soon as the user is logged in I store this data in the following manner:
$window.sessionStorage.user
Because I want to have this sessionStorage
in other tabs I have implemented in my main controller the solution proposed here: https://stackoverflow.com/a/32766809/1281500
Also in this controller I listen to $stateChangeStart
events to check wether the user is logged in or not. If the user is not logged in, I redirect him to the corresponding login page.
My problem comes when I open a new tab in the explorer. As you'll see in the code below, I get the $window.sessionStorage.user
variable from the old tab to the new tab like this:
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]);
}
But the code of the $stateChangeStart
is executed BEFORE the snippet above so the $window.sessionStorage.user
is not available yet and the user is always redirected to the login page even when he's already logged in (at least in the original tab)
$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
}
});
The full controller code is below. How can I have user credentials available in the $stateChangeStart
block?
(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
}
});
}
})();
UPDATE
My application is stateless so it works in the following way:
- The first time the user access to the application I check the username and password are correct (call to Spring Rest service) and at that point I generate a token for the user. This token and user data are both stored in the sessionStorage like
$window.sessionStorage.authToken
and$window.sessionStorage.authToken
. I manage this from another AngularJS Controller (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;
}
);
}
- From now on I send this token with every request to the RESTful application and I have a filter in Spring that checks the token with the user credentials is correct. In case this token doesn't exist or is not valid anymore, Spring Security throws the corresponding Exception and I catch this Exception to redirect to the login page (I don't think this code is important for the current question but I can post it if necessary).
So basically my user only lives in the current sessionStorage
object, I don't have a service to check if the user is logged in or not, I just check the variables I stored during the first log in process.
I hope this helps to clarify the process I little bit more.
Thank you very much.