简体   繁体   中英

AngularJS - prevent not authenticated user from accessing given routes

In my app when user is logged in I have authService which sets internal flag isAuthenticated . Now on every route change I have listener attached to $routeChangeStart event which checks authService.isAuthenticated() . If no it should redirect to login route.

Problem is when user makes page refresh (all the authService settings are lost) and it gets back to login again (while still having valid session on server). This is not what I want.

What I'd like to do is to "block" route change until I get the info if user is authenticated (either from authService which would be immediate, or from server if no info is available in authService , eg after refresh). I have such function in authService

        // returns promise
        currentUser: function() {
            if (authService.isAuthenticated()) {
                return $q.when(authService.loggedUser);
            }
            return $http.get('/session').then(function(response) {
                authService.loggedUser = response.user;
                return $q.when(authService.loggedUser);
            });
        }

and would like to use it in event listener.

    $rootScope.$on("$routeChangeStart", function (event, next, current) {
        if(isRouteRestricted(next)) {
            authService.currentUser().then(null, function() {
                $location.path('/login');
            });
        }
    });

The thing is that it doesn't work as expected. I still get the target route visible for a very short time, and then user gets redirected. I believe it is due to nature of promises, but how to get rid of this "blink" effect?

I'd do something like this in the top level controller , which would be the first controller that's called when the page is refreshed (apologize for typos in the js, I'm a coffeescript guy):

var authCheck = function (event, next, current) {
    if(isRouteRestricted(next)) {
        authService.currentUser().then(null, function() {
            $location.path('/login');
        });
    }
}

authCheck(null, populateNextSomehow).then(function () {
    // all of your controller code, probably in a separate function
});

$rootScope.$on("$routeChangeStart", authCheck);

This will ensure that the controller code cannot be called until the authCheck is complete.

To prevent a user for accessing routes you have to do several things:

First, set your routes and add a property like 'access' : allowAnonymous : true or false

// in app.js
var myApp = angular.module('myApp',['ngResource', 'ngCookies', 'ngRoute']);       
myApp.config(function ($httpProvider, $routeProvider) {
   window.routes = {
        '/Login':
           { templateUrl: '/Account/Login',
             controller: 'AccountController',
             access : {allowAnonymous : true}
           },
        '/MembersPage':
           { templateUrl: '/Home/SomePage,
             controller: SomePageController',
             access: {allowAnonymous:false}
           }
         };

        for (var path in window.routes) {
           $routeProvider.when(path, window.routes[path]);
    }
    $routeProvider.otherwise({ redirectTo: '/Login' });
});   

Second, you must recognize an authenticated user:

There are several ways for doing that but I prefer using the power of AngularJS throughout the use of 'Services'. Therefore, I have created a 'UserService' where we store the current user name and a value-indicating if is authenticated or not.

// in UserService.js
myApp.factory('userService', function () {
var user = {
    isLogged: false,
    username: '',       
};

var reset = function() {
    user.isLogged = false;
    user.username = '';
};

return {
    user: user,
    reset : reset
  };
});

Last thing, capture route changes events and treat them correspondingly:

After we have the service in place, it is time to use it and implement the check functionality for a route. There are several methods that intercept the route change event, but we are interested only in those that occur before the user was redirected so we can check if is authenticated: '$routeChangeStart', '$locationChangeStart'. Here we can check if the route that the user is going to allows anonymous access and if the user is logged in. If the case of failure, we can display an error message and redirect the user to the login page.

// in RootController.js
myApp.controller('RootController',
function ($scope, $route, $routeParams, $location, $rootScope, authenticationService,   
userService, toaster) {
 $scope.user = userService.user;
 $scope.$on('$routeChangeStart', function (e, next, current) {               
     if (next.access != undefined && !next.access.allowAnonymous && !$scope.user.isLogged) {
                $location.path("/Login");                   
            }
        });

        $scope.logout = function () {
            authenticationService.logout()
                .success(function (response) {
                    userService.reset();                       
                    toaster.pop("info", 'You are logged out.', '');
                });
        };

 $rootScope.$on("$locationChangeStart", function (event, next, current) {
  for (var i in window.routes) {
    if (next.indexOf(i) != -1) {
     if (!window.routes[i].access.allowAnonymous && !userService.user.isLogged) {
          toaster.pop("error", 'You are not logged in!', '');
             $location.path("/Login");                                                 
                    }
                }
            }
        });
    });

Complete article is here: http://net-daylight.blogspot.ro/

Hope it helps!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM