2014-09-12 2 views
6

Ich habe eine eckige App, die Autorisierungslogik und UI-Router verwendet, um nicht autorisierte Benutzer für bestimmte Status/Ansichten zu sperren. Ich folge dem Standardansatz, auf ein stateChange-Ereignis zu warten, das meine Berechtigungslogik auslöst. Dies alles funktioniert gut, bis die gefürchtete Seite neu geladen wird.So verzögern Sie ein StateChangeStart-Ereignis, bis eine Auflösung des UI-Routers ausgeführt wird

Ich speichere Sitzungsdaten (einschließlich Genehmigungsstatus) im lokalen Speicher, so dass auf Seite neu geladen ich einen übergeordneten Zustand in ui-Router zum ersten resolve verwenden kann/erhalte den Berechtigungsstatus aus dem lokalen Speicher vor dem Versuch Ansichten zu ändern. Hier ist die Konfiguration meines app Mutterstatusobjekt:

$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 

Und hier ist mein OnStateChange Zuhörer:

//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"); 

     } 
    } 
    }); 

}]); 

Mein Problem ist, dass das stateChangeStart Ereignis auslöst, bevor die app resolve, so dass die Zuhörer Anschläge der Statuswechsel (über die event.preventDefault), und dann lädt meine Lösung die gespeicherten Sitzungsdaten, die oft feststellen, dass der Benutzer die ganze Zeit autorisiert wurde. Wenn ich die Ausführung des Beschlusses vor der Auslösung des Ereignisses verlangen könnte, wäre ich golden.

Irgendwelche Ideen da draußen ???

BTW, ist hier eine ähnliche Frage SO, die unbeantwortet ging: Defer Angular UI Router $stateChangeStart until server authorization response receieved

+1

Diese Antwort kann helfen: http://stackoverflow.com/a/21098923/1873485 – TheSharpieOne

Antwort

-1

Dies ist Client-Seite Sicherheit, die Sie in regelmäßigen Winkel Versionen implementieren können. Ich habe das getestet und getestet. (Bitte finden Sie meinen Artikel hier: - http://www.codeproject.com/Tips/811782/AngularJS-Routing-Security ). Zusätzlich zur clientseitigen Routensicherheit müssen Sie den Zugriff auch serverseitig sichern. Die Sicherheit auf der Client-Seite hilft dabei, eine zusätzliche Hin- und Rückfahrt zum Server zu vermeiden. Wenn jedoch jemand den Browser trickst, sollte die serverseitige Sicherheit den unbefugten Zugriff ablehnen können.

Hoffe, das hilft!

Schritt 1: Definieren Sie Globale Variablen in app-Modul

-definieren Rollen für die Anwendung

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

-Define Route Für unberechtigten Zugriff für die Anwendung

var routeForUnauthorizedAccess = '/SomeAngularRouteForUnauthorizedAccess'; 

Schritt 2: Definieren Sie den Service für die Autorisierung

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(); 
     } 
    } 

}; 
}); 
Verwenden Sie Sicherheit in Routing:

Schritt 3 Diese Funktion ermöglicht den Einsatz verwenden, um alle unsere Hard bisher getan, um sicher die Routen

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, die ursprüngliche Frage nach einer Lösung für 'Ui-Router' und Sie verwenden' ngRoute' in Ihrer Antwort. Ich glaube nicht, dass der Autor das suchte. – treejanitor

1

stellt sich heraus, dass alles, was ich tun musste, war das Laden von Konfigurationsdaten an die .run() bewegen blockieren, anstatt zu versuchen, es in parent app Zustand resolve zu tun.

//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

Ich habe eine gute Möglichkeit, die Entscheidung eines Daten asynchron here während $stateChangeStart in eine andere Antwort gefunden.Hier ist der Code:

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

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

     event.preventDefault(); 

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

Dann können Sie nur daran erinnern, dass Ihre Daten bereits im Dienst initialisiert wird, wie Sie möchten, zum Beispiel:

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

in einem kleinen kommend spät hier, aber ich denke, das wird helfen.

Die $ on-Methode gibt eine Deregistrierungsfunktion für den Listener zurück. Dies ermöglicht das Abbrechen des Ereignisses vor der benutzerdefinierten Verarbeitung im Listener.

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); 
    }; 

Um dies zu nutzen, einfach diese Methode zu einem Dienst hinzufügen und setInterceptedListener ($ scope) nennen.

Verwandte Themen