2014-09-12 4 views
6

Ho un'app angolare che utilizza la logica di autorizzazione e l'ui-router per bloccare utenti non autorizzati per determinati stati/viste. Seguo l'approccio standard di ascolto per un evento stateChange, che attiva la mia logica di autorizzazione. Tutto funziona bene finché non viene ricaricata la pagina temuta.Come posticipare un evento StateChangeStart fino a dopo l'esecuzione di una risoluzione del router uu

Memorizzo i dati di sessione (incluso lo stato di autorizzazione) nella memoria locale in modo che sulla pagina si ricarichi Posso usare uno stato genitore in ui-router per risolvere prima/ottenere lo stato di autorizzazione dalla memoria locale prima di tentare di cambiare vista. Ecco la configurazione del mio oggetto di stato app genitore:

$stateProvider. 
state('app', { 
    url: '/app', 
    abstract: true, 
    controller: 'appCtrl', 
    data: { 
    authorizedRoles: [USER_ROLES.all] 
    }, 
    templateUrl: 'partials/app.html', 
    resolve: { 

    //Try to restore from the previous session before loading any of the app child states 
    RestoredSession: ['SessionService', 
      function(SessionService){ 
       return SessionService.restoreSession(); 
       }] 
    } 
}) 

...various app. child states 

Ed ecco la mia onStateChange ascoltatore:

//listen for a ui.router $stateChangeStart event and test the new path to see if the currentUser 
//is authorized to view that page 

.run(  ['$rootScope', 'AUTH_EVENTS', 'SessionService', 
    function ($rootScope, AUTH_EVENTS, SessionService) { 

    $rootScope.$on('$stateChangeStart', function (event, next) { 
    var authorizedRoles = next.data.authorizedRoles; 
    //If the requested page allows guest access, then continue to stateChange 
    if (authorizedRoles.indexOf('guest') !== -1 || authorizedRoles.indexOf('*') !== -1) return; 

    //If the requested page requires authorization, check login and auth privileges 
    if (!SessionService.isAuthorized(authorizedRoles)) { 

     event.preventDefault(); 

     if (SessionService.existingSession()) { 

     // user is not allowed 
     $rootScope.$broadcast(AUTH_EVENTS.notAuthorized); 
     console.log("User attempted to access page for which he is not authorized"); 

     } else { 

     // user is not logged in 
     $rootScope.$broadcast(AUTH_EVENTS.notLoggedIn); 
     console.log("User attempted to access page when he is not logged in"); 

     } 
    } 
    }); 

}]); 

Il mio problema è che l'evento stateChangeStart sta provocando prima delle app resolve tale che le fermate ascoltatore il cambio di stato (tramite lo event.preventDefault), e quindi la mia risoluzione carica i dati di sessione memorizzati, che spesso stabiliscono che l'utente è stato autorizzato per tutto il tempo. Se potessi richiedere l'esecuzione della risoluzione prima dell'attivazione dell'evento, sarei d'oro.

Qualche idea là fuori ???

BTW, qui è una domanda simile in modo che è rimasta senza risposta: Defer Angular UI Router $stateChangeStart until server authorization response receieved

+1

Questa risposta può aiutare: http://stackoverflow.com/a/21098923/1873485 – TheSharpieOne

risposta

-1

Si tratta di Client Security lato che è possibile implementare nelle versioni angolari regolari. Ho provato e testato questo. (Si prega di trovare il mio articolo qui: - http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security ). Oltre alla sicurezza del percorso lato client, è necessario proteggere anche l'accesso sul lato server. La sicurezza del lato client aiuta ad evitare un ulteriore viaggio di andata e ritorno verso il server. Tuttavia, se qualcuno inganna il browser, la sicurezza del lato server server dovrebbe essere in grado di rifiutare l'accesso non autorizzato.

Spero che questo aiuti!

Fase 1: Definire le variabili globali in app-modulo

ruoli -Definire per l'applicazione

var roles = { 
     superUser: 0, 
     admin: 1, 
     user: 2 
    }; 

-Definire percorso per accessi non autorizzati per l'applicazione

var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess'; 

Fase 2: Definire il servizio per l'autorizzazione

appModule.factory('authorizationService', function ($resource, $q, $rootScope, $location) { 
    return { 
    // We would cache the permission for the session, to avoid roundtrip to server for subsequent requests 
    permissionModel: { permission: {}, isPermissionLoaded: false }, 

    permissionCheck: function (roleCollection) { 
    // we will return a promise . 
      var deferred = $q.defer(); 

    //this is just to keep a pointer to parent scope from within promise scope. 
      var parentPointer = this; 

    //Checking if permisison object(list of roles for logged in user) is already filled from service 
      if (this.permissionModel.isPermissionLoaded) { 

    //Check if the current user has required role to access the route 
        this.getPermission(this.permissionModel, roleCollection, deferred); 
} else { 
    //if permission is not obtained yet, we will get it from server. 
    // 'api/permissionService' is the path of server web service , used for this example. 

        $resource('/api/permissionService').get().$promise.then(function (response) { 
    //when server service responds then we will fill the permission object 
        parentPointer.permissionModel.permission = response; 

    //Indicator is set to true that permission object is filled and can be re-used for subsequent route request for the session of the user 
        parentPointer.permissionModel.isPermissionLoaded = true; 

    //Check if the current user has required role to access the route 
        parentPointer.getPermission(parentPointer.permissionModel, roleCollection, deferred); 
} 
       ); 
} 
      return deferred.promise; 
}, 

     //Method to check if the current user has required role to access the route 
     //'permissionModel' has permission information obtained from server for current user 
     //'roleCollection' is the list of roles which are authorized to access route 
     //'deferred' is the object through which we shall resolve promise 
    getPermission: function (permissionModel, roleCollection, deferred) { 
     var ifPermissionPassed = false; 

     angular.forEach(roleCollection, function (role) { 
      switch (role) { 
       case roles.superUser: 
        if (permissionModel.permission.isSuperUser) { 
         ifPermissionPassed = true; 
        } 
        break; 
       case roles.admin: 
        if (permissionModel.permission.isAdministrator) { 
         ifPermissionPassed = true; 
        } 
        break; 
       case roles.user: 
        if (permissionModel.permission.isUser) { 
         ifPermissionPassed = true; 
        } 
        break; 
       default: 
        ifPermissionPassed = false; 
      } 
     }); 
     if (!ifPermissionPassed) { 
      //If user does not have required access, we will route the user to unauthorized access page 
      $location.path(routeForUnauthorizedAccess); 
      //As there could be some delay when location change event happens, we will keep a watch on $locationChangeSuccess event 
      // and would resolve promise when this event occurs. 
      $rootScope.$on('$locationChangeSuccess', function (next, current) { 
       deferred.resolve(); 
      }); 
     } else { 
      deferred.resolve(); 
     } 
    } 

}; 
}); 

Fase 3: Utilizzare la sicurezza in Routing: Consente l'uso utilizzare tutti i nostri Hardword fatto finora, per fissare i percorsi

var appModule = angular.module("appModule", ['ngRoute', 'ngResource']) 
    .config(function ($routeProvider, $locationProvider) { 
     $routeProvider 
      .when('/superUserSpecificRoute', { 
       templateUrl: '/templates/superUser.html',//path of the view/template of route 
       caseInsensitiveMatch: true, 
       controller: 'superUserController',//angular controller which would be used for the route 
       resolve: {//Here we would use all the hardwork we have done above and make call to the authorization Service 
        //resolve is a great feature in angular, which ensures that a route controller(in this case superUserController) is invoked for a route only after the promises mentioned under it are resolved. 
        permission: function(authorizationService, $route) { 
         return authorizationService.permissionCheck([roles.superUser]); 
        }, 
       } 
      }) 
     .when('/userSpecificRoute', { 
      templateUrl: '/templates/user.html', 
      caseInsensitiveMatch: true, 
      controller: 'userController', 
      resolve: { 
       permission: function (authorizationService, $route) { 
        return authorizationService.permissionCheck([roles.user]); 
       }, 
      } 
      }) 
      .when('/adminSpecificRoute', { 
       templateUrl: '/templates/admin.html', 
       caseInsensitiveMatch: true, 
       controller: 'adminController', 
       resolve: { 
        permission: function(authorizationService, $route) { 
         return authorizationService.permissionCheck([roles.admin]); 
        }, 
       } 
      }) 
      .when('/adminSuperUserSpecificRoute', { 
       templateUrl: '/templates/adminSuperUser.html', 
       caseInsensitiveMatch: true, 
       controller: 'adminSuperUserController', 
       resolve: { 
        permission: function(authorizationService, $route) { 
         return authorizationService.permissionCheck([roles.admin,roles.superUser]); 
        }, 
       } 
      }) 
    }); 
+1

Hmm, la domanda originale chiedeva una soluzione per 'ui-router' e si sta utilizzando' ngRoute' nella risposta. Non penso che questo sia ciò che l'autore stava cercando. – treejanitor

1

scopre che tutto quello che dovevo fare era spostare il caricamento dei dati di configurazione al .run() bloccare invece di provare a farlo nello dello stato .

//listen for a ui.router $stateChangeStart event and test the new path to see if the currentUser 
//is authorized to view that page 
.run(  ['$rootScope', 'AUTH_EVENTS','SessionService', 'localStorageService', 
    function ($rootScope, AUTH_EVENTS, SessionService, localStorageService) 
    { 
    $rootScope.$on('$stateChangeStart', function (event, next) { 

    //function to check to see if the currentUser has one of the required roles to authorize the next state. 
    var checkAuthorization = function(authorizedRoles){ 

     //If the requested page allows guest access, then continue to stateChange 
     if (authorizedRoles.indexOf('guest') !== -1 || authorizedRoles.indexOf('*') !== -1) return; 
     //If the requested page requires authorization, check login and auth privileges 
     if (!SessionService.isAuthorized(authorizedRoles)) { 
      event.preventDefault(); 
      if (SessionService.existingSession()) { 
      // user is not allowed 
      $rootScope.$broadcast(AUTH_EVENTS.notAuthorized); 
      console.log("User attempted to access page for which he is not authorized"); 
      } else { 
      // user is not logged in 
      $rootScope.$broadcast(AUTH_EVENTS.notLoggedIn); 
      console.log("User attempted to access page when he is not logged in"); 
      } 
     } 
     }; 

    //Before calling checkAuthorization(), test to see if the state change was triggered by a reload 
    //If so, load config data before triggering the `checkAuthorization()` function. 
    if (SessionService.freshLoad === true || typeof SessionService.freshLoad === 'undefined'){ 
     SessionService.freshLoad = false; 
     var storedUser = localStorageService.get('currentUser'); 

     //If we have a stored user but no existing session, then we know that we have stored 
     //user data to reload before the checkAuthorization() function. 
     if (typeof storedUser !== "undefined" && storedUser !== null && !SessionService.existingSession()) { 
     SessionService.restoreSession(); 
     } 
    } 

    checkAuthorization(next.data.authorizedRoles); 

    }); 

}]); 
0

ho trovato un buon modo di risolvere un dati in modo asincrono durante $stateChangeStart in un'altra risposta here.Ecco il codice:

rootScope.$on("$stateChangeStart", function (event, toState, toParams, fromState) { 

    if (dataService.isInitialized()) { 
     proceedAsUsual(); 
    } 
    else { 

     event.preventDefault(); 

     dataService.intialize().success(function() { 
       $state.go(toState, toParams); 
     }); 
    } 
}); 

allora si può solo ricordare che i dati sono già inizializzato nel servizio nel modo desiderato, ad esempio:

function dataService() { 

    var initialized = false; 

    return { 
     initialize: initialize, 
     isInitialized: isInitialized 
    } 

    function intialize() { 

     return $http.get(...) 
        .success(function(response) { 
          initialized=true; 
        }); 

    } 

    function isInitialized() { 
     return initialized; 
    } 
}; 
0

Venendo in un po 'in ritardo qui, ma penso che questo aiuterà.

Il metodo $ on restituisce una funzione di deregistrazione per l'ascoltatore. Ciò consente l'annullamento dell'evento prima della gestione personalizzata nell'ascoltatore.

var setInterceptedListener = function($scope) { 
    var removeListener = $rootScope.$on('$stateChangeStart', 
     function (event, toState, toParams, fromState, fromParams) { 
      // cancel state change 
      event.preventDefault(); 

      // mock prompt for user input 
      Prompt.continue('Continue?').then(function(result) { 
       // if yes then deregister the listener in order to proceed. 
       if (result == 'yes') { 
        removeListener(); 
        $state.go(toState, toParams); 
       } 
      }); 
     }); 

     // deregister on scope teardown 
     $scope.$on("$destroy", removeListener); 
    }; 

Per utilizzare questo, semplice aggiungere questo metodo per un servizio e chiamare setInterceptedListener ($ portata).

Problemi correlati