Authentification de connexion AngularJS ui-router

376

Je suis nouveau sur AngularJS et je suis un peu confus quant à la façon dont je peux utiliser angular- "ui-router" dans le scénario suivant:

Je crée une application web qui se compose de deux sections. La première section est la page d'accueil avec ses vues de connexion et d'inscription, et la deuxième section est le tableau de bord (après une connexion réussie).

J'ai créé un index.htmlpour la section d'accueil avec son application angulaire et sa ui-routerconfiguration à gérer /loginet ses /signupvues, et il existe un autre fichier dashboard.htmlpour la section du tableau de bord avec son application et sa ui-routerconfiguration pour gérer de nombreuses sous-vues.

Maintenant, j'ai terminé la section du tableau de bord et je ne sais pas comment combiner les deux sections avec leurs différentes applications angulaires. Comment puis-je dire à l'application domestique de se rediriger vers l'application de tableau de bord?

Ahmed Hashem
la source
1
Pouvez-vous partager du code avec nous?
Chancho
6
@Chancho Je pense qu'il ne s'agit pas de code, vraiment je ne sais pas quel code dois-je partager.
Ahmed Hashem
oui, merci de partager le code, question très générique ...
Alireza

Réponses:

607

Je suis en train de faire une démo plus agréable ainsi que de nettoyer certains de ces services en un module utilisable, mais voici ce que j'ai trouvé. Il s'agit d'un processus complexe pour contourner certaines mises en garde, alors accrochez-vous. Vous devrez le décomposer en plusieurs morceaux.

Jetez un oeil à ce plunk .

Tout d'abord, vous avez besoin d'un service pour stocker l'identité de l'utilisateur. J'appelle ça principal. Il peut être vérifié pour voir si l'utilisateur est connecté et, sur demande, il peut résoudre un objet qui représente les informations essentielles sur l'identité de l'utilisateur. Cela peut être tout ce dont vous avez besoin, mais l'essentiel serait un nom d'affichage, un nom d'utilisateur, éventuellement un e-mail et les rôles auxquels un utilisateur appartient (si cela s'applique à votre application). Le directeur dispose également de méthodes pour effectuer des vérifications de rôle.

.factory('principal', ['$q', '$http', '$timeout',
  function($q, $http, $timeout) {
    var _identity = undefined,
      _authenticated = false;

    return {
      isIdentityResolved: function() {
        return angular.isDefined(_identity);
      },
      isAuthenticated: function() {
        return _authenticated;
      },
      isInRole: function(role) {
        if (!_authenticated || !_identity.roles) return false;

        return _identity.roles.indexOf(role) != -1;
      },
      isInAnyRole: function(roles) {
        if (!_authenticated || !_identity.roles) return false;

        for (var i = 0; i < roles.length; i++) {
          if (this.isInRole(roles[i])) return true;
        }

        return false;
      },
      authenticate: function(identity) {
        _identity = identity;
        _authenticated = identity != null;
      },
      identity: function(force) {
        var deferred = $q.defer();

        if (force === true) _identity = undefined;

        // check and see if we have retrieved the 
        // identity data from the server. if we have, 
        // reuse it by immediately resolving
        if (angular.isDefined(_identity)) {
          deferred.resolve(_identity);

          return deferred.promise;
        }

        // otherwise, retrieve the identity data from the
        // server, update the identity object, and then 
        // resolve.
        //           $http.get('/svc/account/identity', 
        //                     { ignoreErrors: true })
        //                .success(function(data) {
        //                    _identity = data;
        //                    _authenticated = true;
        //                    deferred.resolve(_identity);
        //                })
        //                .error(function () {
        //                    _identity = null;
        //                    _authenticated = false;
        //                    deferred.resolve(_identity);
        //                });

        // for the sake of the demo, fake the lookup
        // by using a timeout to create a valid
        // fake identity. in reality,  you'll want 
        // something more like the $http request
        // commented out above. in this example, we fake 
        // looking up to find the user is
        // not logged in
        var self = this;
        $timeout(function() {
          self.authenticate(null);
          deferred.resolve(_identity);
        }, 1000);

        return deferred.promise;
      }
    };
  }
])

Deuxièmement, vous avez besoin d'un service qui vérifie l'état dans lequel l'utilisateur veut aller, s'assure qu'il est connecté (si nécessaire; pas nécessaire pour la connexion, la réinitialisation du mot de passe, etc.), puis effectue une vérification des rôles (si votre application en a besoin). S'ils ne sont pas authentifiés, envoyez-les sur la page de connexion. S'ils sont authentifiés, mais échouent à une vérification de rôle, envoyez-les vers une page d'accès refusé. J'appelle ce service authorization.

.factory('authorization', ['$rootScope', '$state', 'principal',
  function($rootScope, $state, principal) {
    return {
      authorize: function() {
        return principal.identity()
          .then(function() {
            var isAuthenticated = principal.isAuthenticated();

            if ($rootScope.toState.data.roles
                && $rootScope.toState
                             .data.roles.length > 0 
                && !principal.isInAnyRole(
                   $rootScope.toState.data.roles))
            {
              if (isAuthenticated) {
                  // user is signed in but not
                  // authorized for desired state
                  $state.go('accessdenied');
              } else {
                // user is not authenticated. Stow
                // the state they wanted before you
                // send them to the sign-in state, so
                // you can return them when you're done
                $rootScope.returnToState
                    = $rootScope.toState;
                $rootScope.returnToStateParams
                    = $rootScope.toStateParams;

                // now, send them to the signin state
                // so they can log in
                $state.go('signin');
              }
            }
          });
      }
    };
  }
])

Maintenant , tout ce que vous devez faire est d' écouter dans le ui-router« s $stateChangeStart. Cela vous donne la possibilité d'examiner l'état actuel, l'état dans lequel ils veulent aller et d'insérer votre vérification d'autorisation. En cas d'échec, vous pouvez annuler la transition de l'itinéraire ou passer à un autre itinéraire.

.run(['$rootScope', '$state', '$stateParams', 
      'authorization', 'principal',
    function($rootScope, $state, $stateParams, 
             authorization, principal)
{
      $rootScope.$on('$stateChangeStart', 
          function(event, toState, toStateParams)
      {
        // track the state the user wants to go to; 
        // authorization service needs this
        $rootScope.toState = toState;
        $rootScope.toStateParams = toStateParams;
        // if the principal is resolved, do an 
        // authorization check immediately. otherwise,
        // it'll be done when the state it resolved.
        if (principal.isIdentityResolved()) 
            authorization.authorize();
      });
    }
  ]);

La partie délicate du suivi de l'identité d'un utilisateur consiste à la rechercher si vous vous êtes déjà authentifié (par exemple, vous visitez la page après une session précédente et avez enregistré un jeton d'authentification dans un cookie, ou vous avez peut-être actualisé une page, ou déposé sur une URL à partir d'un lien). En raison de la façon dont cela ui-routerfonctionne, vous devez résoudre votre identité une fois, avant vos vérifications d'authentification. Vous pouvez le faire en utilisant l' resolveoption dans votre configuration d'état. J'ai un état parent pour le site dont tous les états héritent, ce qui oblige le principal à être résolu avant que quoi que ce soit d'autre se produise.

$stateProvider.state('site', {
  'abstract': true,
  resolve: {
    authorize: ['authorization',
      function(authorization) {
        return authorization.authorize();
      }
    ]
  },
  template: '<div ui-view />'
})

Il y a un autre problème ici ... resolven'est appelé qu'une seule fois. Une fois votre promesse de recherche d'identité terminée, elle ne réexécutera pas le délégué de résolution. Nous devons donc effectuer vos vérifications d'authentification à deux endroits: une fois conformément à votre promesse d'identité résolue resolve, qui couvre la première fois que votre application se charge, et une fois $stateChangeStartsi la résolution a été effectuée, qui couvre chaque fois que vous naviguez dans les États.

OK, qu'avons-nous fait jusqu'à présent?

  1. Nous vérifions pour voir quand l'application se charge si l'utilisateur est connecté.
  2. Nous suivons les informations sur l'utilisateur connecté.
  3. Nous les redirigeons vers l'état de connexion pour les États qui nécessitent que l'utilisateur soit connecté.
  4. Nous les redirigeons vers un état d'accès refusé s'ils ne sont pas autorisés à y accéder.
  5. Nous avons un mécanisme pour rediriger les utilisateurs vers l'état d'origine qu'ils ont demandé, si nous en avions besoin pour se connecter.
  6. Nous pouvons déconnecter un utilisateur (doit être connecté de concert avec tout code client ou serveur qui gère votre ticket d'authentification).
  7. Nous n'avons pas besoin de renvoyer les utilisateurs vers la page de connexion chaque fois qu'ils rechargent leur navigateur ou déposent un lien.

Où allons-nous à partir d'ici? Eh bien, vous pouvez organiser vos états en régions qui nécessitent une connexion. Vous pouvez exiger des utilisateurs authentifiés / autorisés en ajoutant dataavec rolesà ces états (ou un parent d'entre eux, si vous souhaitez utiliser l'héritage). Ici, nous limitons une ressource aux administrateurs:

.state('restricted', {
    parent: 'site',
    url: '/restricted',
    data: {
      roles: ['Admin']
    },
    views: {
      'content@': {
        templateUrl: 'restricted.html'
      }
    }
  })

Vous pouvez désormais contrôler état par état quels utilisateurs peuvent accéder à un itinéraire. D'autres soucis? Peut-être varier seulement une partie d'une vue selon qu'ils sont connectés ou non? Aucun problème. Utilisez le principal.isAuthenticated()ou même principal.isInRole()avec l'une des nombreuses façons dont vous pouvez conditionnellement afficher un modèle ou un élément.

Tout d'abord, injectez-le principaldans un contrôleur ou autre, et collez-le sur la portée afin de pouvoir l'utiliser facilement dans votre vue:

.scope('HomeCtrl', ['$scope', 'principal', 
    function($scope, principal)
{
  $scope.principal = principal;
});

Afficher ou masquer un élément:

<div ng-show="principal.isAuthenticated()">
   I'm logged in
</div>
<div ng-hide="principal.isAuthenticated()">
  I'm not logged in
</div>

Etc., etc., etc. Quoi qu'il en soit, dans votre exemple d'application, vous auriez un état pour la page d'accueil qui permettrait aux utilisateurs non authentifiés de passer. Ils peuvent avoir des liens vers les états de connexion ou d'inscription, ou avoir ces formulaires intégrés dans cette page. Tout ce qui vous convient.

Les pages du tableau de bord peuvent toutes hériter d'un état qui requiert que les utilisateurs soient connectés et, par exemple, Usermembres du rôle. Tous les éléments d'autorisation dont nous avons discuté en découleraient.

moribvndvs
la source
28
Merci, cela m'a vraiment aidé à rassembler mon propre code. Sur une note latérale, si vous obtenez une boucle de routage infinie (bug du routeur UI), essayez à la $location.pathplace de $state.go.
jvannistelrooy
2
C'est une excellente réponse et cela m'a beaucoup aidé. Lorsque je mets user = principal dans mon contrôleur et que j'appelle say user.identity (). Name à mon avis pour obtenir le nom d'utilisateur actuellement connecté, il me semble que je ne reçois que l'objet promis {then: fn, catch: fn, enfin :} retourné et non l'objet _identity réel. Si j'utilise user.identity.then (fn (user)), je peux obtenir l'objet utilisateur, mais cela semble être beaucoup de code pour la vue.
Mark
4
@ Ir1sh Je voudrais d'abord résoudre l'identité dans le contrôleur et l'assigner à $scope.uservotre thenfonction. Vous pouvez toujours faire référence userdans vos vues; une fois résolu, la vue sera mise à jour.
moribvndvs
2
@HackedByChinese Je pense que votre démo ne fonctionne plus.
Blowsie
7
@jvannistelrooy J'ai eu des problèmes avec go (), mais après l'avoir mis dans le puis après avoir appelé une fonction noop comme celle-ci $q.when(angular.noop).then(function(){$state.go('myState'), tout fonctionne comme prévu. Si j'appelle $state.goalors qu'une autre transition d'état n'est pas terminée, cela ne fonctionnera pas (je pense que c'est la raison pour laquelle cela ne fonctionnera pas).
Sebastian
120

À mon avis, les solutions publiées jusqu'à présent sont inutilement compliquées. Il existe un moyen plus simple. La documentation deui-router dit écouter $locationChangeSuccesset utiliser$urlRouter.sync() pour vérifier une transition d'état, l'arrêter ou la reprendre. Mais même cela ne fonctionne pas.

Cependant, voici deux alternatives simples. Choisissez-en un:

Solution 1: écouter $locationChangeSuccess

Vous pouvez écouter $locationChangeSuccesset exécuter une logique, même une logique asynchrone. Sur la base de cette logique, vous pouvez laisser la fonction retourner indéfinie, ce qui entraînera la transition d'état à continuer normalement, ou vous pouvez le faire $state.go('logInPage'), si l'utilisateur doit être authentifié. Voici un exemple:

angular.module('App', ['ui.router'])

// In the run phase of your Angular application  
.run(function($rootScope, user, $state) {

  // Listen to '$locationChangeSuccess', not '$stateChangeStart'
  $rootScope.$on('$locationChangeSuccess', function() {
    user
      .logIn()
      .catch(function() {
        // log-in promise failed. Redirect to log-in page.
        $state.go('logInPage')
      })
  })
})

Gardez à l'esprit que cela n'empêche pas réellement l'état cible de se charger, mais il redirige vers la page de connexion si l'utilisateur n'est pas autorisé. Ce n'est pas grave car la vraie protection est sur le serveur, de toute façon.

Solution 2: utilisation de l'état resolve

Dans cette solution, vous utilisez la ui-routerfonction de résolution .

Vous rejetez essentiellement la promesse resolvesi l'utilisateur n'est pas authentifié, puis vous les redirigez vers la page de connexion.

Voici comment ça se passe:

angular.module('App', ['ui.router'])

.config(
  function($stateProvider) {
    $stateProvider
      .state('logInPage', {
        url: '/logInPage',
        templateUrl: 'sections/logInPage.html',
        controller: 'logInPageCtrl',
      })
      .state('myProtectedContent', {
        url: '/myProtectedContent',
        templateUrl: 'sections/myProtectedContent.html',
        controller: 'myProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })
      .state('alsoProtectedContent', {
        url: '/alsoProtectedContent',
        templateUrl: 'sections/alsoProtectedContent.html',
        controller: 'alsoProtectedContentCtrl',
        resolve: { authenticate: authenticate }
      })

    function authenticate($q, user, $state, $timeout) {
      if (user.isAuthenticated()) {
        // Resolve the promise successfully
        return $q.when()
      } else {
        // The next bit of code is asynchronously tricky.

        $timeout(function() {
          // This code runs after the authentication promise has been rejected.
          // Go to the log-in page
          $state.go('logInPage')
        })

        // Reject the authentication promise to prevent the state from loading
        return $q.reject()
      }
    }
  }
)

Contrairement à la première solution, cette solution empêche le chargement de l'état cible.

MK Safi
la source
6
@FredLackey indique que l'utilisateur non authentifié est présent state A. Ils cliquent sur un lien pour y accéder, protected state Bmais vous souhaitez les rediriger vers logInPage. S'il n'y a pas $timeout, ui-routerarrêtera simplement toutes les transitions d'état, de sorte que l'utilisateur serait coincé state A. Le $timeoutpermet ui-routerd'abord d'empêcher la transition initiale vers protected state Bcar la résolution a été rejetée et après cela, il redirige vers logInPage.
MK Safi
Où la authenticatefonction est-elle réellement appelée?
CodyBugstein
La authenticatefonction @Imray est passée en paramètre à ui-router. Vous n'avez pas à l'appeler vous-même. ui-routerl'appelle.
MK Safi
Pourquoi utilisez-vous '$ locationChangeSuccess' au lieu de '$ stateChangeStart'?
Draex_
@ PeterDraexDräxler Je suivais principalement la documentation. Avez-vous remarqué une différence en utilisant $stateChangeStart?
MK Safi
42

La solution la plus simple consiste à utiliser $stateChangeStartet event.preventDefault()à annuler le changement d'état lorsque l'utilisateur n'est pas authentifié et à le rediriger vers l' état d' authentification qui est la page de connexion.

angular
  .module('myApp', [
    'ui.router',
  ])
    .run(['$rootScope', 'User', '$state',
    function ($rootScope, User, $state) {
      $rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
        if (toState.name !== 'auth' && !User.authenticaded()) {
          event.preventDefault();
          $state.go('auth');
        }
      });
    }]
  );
sebest
la source
6
Je ne pense pas que cela fonctionnera si User.authenticaded () est un appel asynchrone. C'est le Saint Graal que tout le monde recherche. Par exemple, si chaque état à l' exception de "login" est sécurisé, je veux confirmer que l'utilisateur est toujours authentifié AVANT de charger un état. L'utilisation de resolves suce car ils ne se résolvent qu'une seule fois, et pour empêcher le chargement des états enfants, vous devez injecter la résolution dans CHAQUE ENFANT .
Jason
authentifié n'est pas un appel asynchrone dans mon cas: `this.authenticaded = function () {if (this.currentAccountID! == null) {return true; } return false; }; `
2015
Selon: stackoverflow.com/a/38374313/849829 , «exécuter» vient bien au-dessus des «services» et donc des problèmes. La vérification du statut authentifié sur localstorage semble être une bonne approche.
Deepak Thomas
22

Je pense que vous avez besoin d'un servicequi gère le processus d'authentification (et son stockage).

Dans ce service, vous aurez besoin de quelques méthodes de base:

  • isAuthenticated()
  • login()
  • logout()
  • etc ...

Ce service doit être injecté dans vos contrôleurs de chaque module:

  • Dans votre section de tableau de bord, utilisez ce service pour vérifier si l'utilisateur est authentifié ( service.isAuthenticated()méthode). sinon, redirigez vers / login
  • Dans votre section de connexion, utilisez simplement les données du formulaire pour authentifier l'utilisateur via votre service.login()méthode

Un bon et robuste exemple de ce comportement est le projet angular-app et en particulier son module de sécurité qui est basé sur le génial module HTTP Auth Interceptor

J'espère que cela t'aides

Cétia
la source
21

J'ai créé ce module pour aider à faire de ce processus un morceau de gâteau

Vous pouvez faire des choses comme:

$routeProvider
  .state('secret',
    {
      ...
      permissions: {
        only: ['admin', 'god']
      }
    });

Ou aussi

$routeProvider
  .state('userpanel',
    {
      ...
      permissions: {
        except: ['not-logged-in']
      }
    });

Il est tout neuf mais mérite d'être vérifié!

https://github.com/Narzerus/angular-permission

Rafael Vidaurre
la source
2
ce qui m'empêche de modifier la source au moment de l'exécution et de supprimer votre «administrateur» || «dieu» et continue?
Pogrindis du
12
J'espère que toutes les demandes de données nécessitant une autorisation sont également vérifiées sur le serveur.
Ben Ripley
24
Ce n'est pas destiné à la sécurité, l'autorisation côté client ne l'est jamais car vous pouvez toujours modifier les valeurs. Vous pouvez même intercepter les réponses du côté serveur et les évaluer comme "autorisées". Le point des autorisations / autorisations côté client est d'éviter de laisser l'utilisateur faire des choses interdites à des fins d'UX. Par exemple, si vous gérez une action réservée aux administrateurs, même si l'utilisateur trompe le client par malveillance pour autoriser l'envoi d'une demande restreinte au serveur, le serveur renvoie toujours une réponse 401. C'est bien sûr toujours la responsabilité de l'api en cours de mise en œuvre @BenRipley en effet
Rafael Vidaurre
3
Excellente réponse à la question Rafael. Protégez toujours les API, car le front-end est la chose la plus ingénieuse et la plus usurpée qui soit.
Frankie Loscavio
1
Ce problème avec l'historique est résolu depuis un bon moment maintenant @Bohdan. Vous pouvez l'utiliser en toute sécurité même avec des extras ui-router.
masterspambot
16

Je voulais partager une autre solution fonctionnant avec le routeur ui 1.0.0.X

Comme vous le savez peut-être, stateChangeStart et stateChangeSuccess sont désormais obsolètes. https://github.com/angular-ui/ui-router/issues/2655

À la place, vous devez utiliser $ transitions http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html

Voici comment je l'ai réalisé:

J'ai d'abord et AuthService avec quelques fonctions utiles

angular.module('myApp')

        .factory('AuthService',
                ['$http', '$cookies', '$rootScope',
                    function ($http, $cookies, $rootScope) {
                        var service = {};

                        // Authenticates throug a rest service
                        service.authenticate = function (username, password, callback) {

                            $http.post('api/login', {username: username, password: password})
                                    .success(function (response) {
                                        callback(response);
                                    });
                        };

                        // Creates a cookie and set the Authorization header
                        service.setCredentials = function (response) {
                            $rootScope.globals = response.token;

                            $http.defaults.headers.common['Authorization'] = 'Bearer ' + response.token;
                            $cookies.put('globals', $rootScope.globals);
                        };

                        // Checks if it's authenticated
                        service.isAuthenticated = function() {
                            return !($cookies.get('globals') === undefined);
                        };

                        // Clear credentials when logout
                        service.clearCredentials = function () {
                            $rootScope.globals = undefined;
                            $cookies.remove('globals');
                            $http.defaults.headers.common.Authorization = 'Bearer ';
                        };

                        return service;
                    }]);

Ensuite, j'ai cette configuration:

angular.module('myApp', [
    'ui.router',
    'ngCookies'
])
        .config(['$stateProvider', '$urlRouterProvider',
            function ($stateProvider, $urlRouterProvider) {
                $urlRouterProvider.otherwise('/resumen');
                $stateProvider
                        .state("dashboard", {
                            url: "/dashboard",
                            templateUrl: "partials/dashboard.html",
                            controller: "dashCtrl",
                            data: {
                                authRequired: true
                            }
                        })
                        .state("login", {
                            url: "/login",
                            templateUrl: "partials/login.html",
                            controller: "loginController"
                        })
            }])

        .run(['$rootScope', '$transitions', '$state', '$cookies', '$http', 'AuthService',
            function ($rootScope, $transitions, $state, $cookies, $http, AuthService) {

                // keep user logged in after page refresh
                $rootScope.globals = $cookies.get('globals') || {};
                $http.defaults.headers.common['Authorization'] = 'Bearer ' + $rootScope.globals;

                $transitions.onStart({
                    to: function (state) {
                        return state.data != null && state.data.authRequired === true;
                    }
                }, function () {
                    if (!AuthService.isAuthenticated()) {
                        return $state.target("login");
                    }
                });
            }]);

Vous pouvez voir que j'utilise

data: {
   authRequired: true
}

pour marquer l'état uniquement accessible s'il est authentifié.

puis, sur le .run, j'utilise les transitions pour vérifier l'état d'authentification

$transitions.onStart({
    to: function (state) {
        return state.data != null && state.data.authRequired === true;
    }
}, function () {
    if (!AuthService.isAuthenticated()) {
        return $state.target("login");
    }
});

Je construis cet exemple en utilisant du code trouvé dans la documentation de $ transitions. Je suis assez nouveau avec le routeur ui mais ça marche.

J'espère que cela peut aider n'importe qui.

Sergio Fernandez
la source
C'est idéal pour ceux qui utilisent le nouveau routeur. Merci!
mtro
5

Voici comment nous sommes sortis de la boucle de routage infinie et toujours utilisés $state.goau lieu de$location.path

if('401' !== toState.name) {
  if (principal.isIdentityResolved()) authorization.authorize();
}
Jason Girdner
la source
1
Quelqu'un sait-il pourquoi lors de l'utilisation de la réponse / configuration acceptée décrite ci-dessus, la barre d'adresse n'affiche plus l'url et tous les fragments et paramètres de chaîne de requête? Depuis sa mise en œuvre, la barre d'adresse ne permet plus de mettre notre application en signet.
Frankie Loscavio
1
N'est-ce pas censé être un commentaire sur l'une des réponses existantes? Parce qu'il n'y a pas un tel code dans OP et il n'est même pas clair à quelle réponse / à quel code il s'agit
TJ
3

J'ai une autre solution: cette solution fonctionne parfaitement lorsque vous n'avez que du contenu que vous souhaitez afficher lorsque vous êtes connecté. Définissez une règle dans laquelle vous vérifiez si vous êtes connecté et ce n'est pas le chemin des routes de la liste blanche.

$urlRouterProvider.rule(function ($injector, $location) {
   var UserService = $injector.get('UserService');
   var path = $location.path(), normalized = path.toLowerCase();

   if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
     $location.path('/login/signin');
   }
});

Dans mon exemple, je demande si je ne suis pas connecté et que l'itinéraire actuel que je souhaite router ne fait pas partie de `/ login ', car mes itinéraires sur liste blanche sont les suivants

/login/signup // registering new user
/login/signin // login to app

J'ai donc un accès instantané à ces deux itinéraires et tous les autres itinéraires seront vérifiés si vous êtes en ligne.

Voici tout mon fichier de routage pour le module de connexion

export default (
  $stateProvider,
  $locationProvider,
  $urlRouterProvider
) => {

  $stateProvider.state('login', {
    parent: 'app',
    url: '/login',
    abstract: true,
    template: '<ui-view></ui-view>'
  })

  $stateProvider.state('signin', {
    parent: 'login',
    url: '/signin',
    template: '<login-signin-directive></login-signin-directive>'
  });

  $stateProvider.state('lock', {
    parent: 'login',
    url: '/lock',
    template: '<login-lock-directive></login-lock-directive>'
  });

  $stateProvider.state('signup', {
    parent: 'login',
    url: '/signup',
    template: '<login-signup-directive></login-signup-directive>'
  });

  $urlRouterProvider.rule(function ($injector, $location) {
    var UserService = $injector.get('UserService');
    var path = $location.path();

    if (!UserService.isLoggedIn() && path.indexOf('login') === -1) {
         $location.path('/login/signin');
    }
  });

  $urlRouterProvider.otherwise('/error/not-found');
}

() => { /* code */ } est la syntaxe ES6, utilisez plutôt function() { /* code */ }

Chris Incoqnito
la source
3

Utiliser $ http Interceptor

En utilisant un intercepteur $ http, vous pouvez envoyer des en-têtes à Back-end ou l'inverse et faire vos vérifications de cette façon.

Excellent article sur les intercepteurs $ http

Exemple:

$httpProvider.interceptors.push(function ($q) {
        return {
            'response': function (response) {

                // TODO Create check for user authentication. With every request send "headers" or do some other check
                return response;
            },
            'responseError': function (reject) {

                // Forbidden
                if(reject.status == 403) {
                    console.log('This page is forbidden.');
                    window.location = '/';
                // Unauthorized
                } else if(reject.status == 401) {
                    console.log("You're not authorized to view this page.");
                    window.location = '/';
                }

                return $q.reject(reject);
            }
        };
    });

Mettez ceci dans votre fonction .config ou .run.

TSlegaitis
la source
2

Vous aurez d'abord besoin d'un service que vous pouvez injecter dans vos contrôleurs qui a une idée de l'état d'authentification de l'application. La persistance des détails d'authentification avec le stockage local est une manière décente de l'aborder.

Ensuite, vous devrez vérifier l'état d'authentification juste avant les changements d'état. Étant donné que votre application possède certaines pages qui doivent être authentifiées et d'autres qui ne le font pas, créez un itinéraire parent qui vérifie l'authentification et faites en sorte que toutes les autres pages qui nécessitent la même chose soient des enfants de ce parent.

Enfin, vous aurez besoin d'un moyen de savoir si votre utilisateur actuellement connecté peut effectuer certaines opérations. Ceci peut être réalisé en ajoutant une fonction «can» à votre service d'authentification. Peut prendre deux paramètres: - action - obligatoire - (c'est-à-dire 'manage_dashboards' ou 'create_new_dashboard') - objet - facultatif - objet en cours d'opération. Par exemple, si vous aviez un objet de tableau de bord, vous souhaiterez peut-être vérifier si dashboard.ownerId === connectedInUser.id. (Bien sûr, les informations transmises par le client ne doivent jamais être approuvées et vous devez toujours les vérifier sur le serveur avant de les écrire dans votre base de données).

angular.module('myApp', ['ngStorage']).config([
   '$stateProvider',
function(
   $stateProvider
) {
   $stateProvider
     .state('home', {...}) //not authed
     .state('sign-up', {...}) //not authed
     .state('login', {...}) //not authed
     .state('authed', {...}) //authed, make all authed states children
     .state('authed.dashboard', {...})
}])
.service('context', [
   '$localStorage',
function(
   $localStorage
) {
   var _user = $localStorage.get('user');
   return {
      getUser: function() {
         return _user;
      },
      authed: function() {
         return (_user !== null);
      },
      // server should return some kind of token so the app 
      // can continue to load authenticated content without having to
      // re-authenticate each time
      login: function() {
         return $http.post('/login.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      // this request should expire that token, rendering it useless
      // for requests outside of this session
      logout: function() {
         return $http.post('logout.json').then(function(reply) {
            if (reply.authenticated === true) {
               $localStorage.set(_userKey, reply.user);
            }
         });
      },
      can: function(action, object) {
         if (!this.authed()) {
            return false;
         }

         var user = this.getUser();

         if (user && user.type === 'admin') {
             return true;
         }

         switch(action) {
            case 'manage_dashboards':
               return (user.type === 'manager');
         }

         return false;


      }
   }
}])
.controller('AuthCtrl', [
   'context', 
   '$scope', 
function(
   context, 
   $scope
) {
   $scope.$root.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams) {
      //only require auth if we're moving to another authed page
      if (toState && toState.name.indexOf('authed') > -1) {
         requireAuth();
      }
   });

   function requireAuth() {
      if (!context.authed()) {
         $state.go('login');
      }
   }
}]

** AVERTISSEMENT: Le code ci-dessus est un pseudo-code et est livré sans aucune garantie **

colefner
la source