378 votes

AngularJS ui-router login authentification

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

Je suis en train de construire 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 seconde section est le tableau de bord (après une connexion réussie).

J'ai créé un index.html pour la section "home" avec son application angulaire et ui-router configurer pour gérer /login et /signup vues, et il y a un autre fichier dashboard.html pour la section du tableau de bord avec son application et ui-router pour gérer de nombreuses vues secondaires.

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 home de rediriger vers l'application dashboard ?

1 votes

Pouvez-vous partager un peu de code avec nous ?

6 votes

@Chancho Je pense que ce n'est pas une question de code, vraiment je ne sais pas quel code je devrais partager.

0 votes

Oui, merci de partager le code, question très générique...

16voto

Sergio Fernandez Points 161

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

Au lieu de cela, vous devez utiliser $transitions http://angular-ui.github.io/ui-router/1.0.0-alpha.1/interfaces/transition.ihookregistry.html

Voici comment j'y suis parvenu :

Tout d'abord, j'ai 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;
                    }]);

J'ai alors la configuration suivante :

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 indiquer que l'état n'est accessible que s'il est authentifié.

puis, sur le .courir J'utilise les transitions pour vérifier l'état de l'authentification.

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

J'ai construit cet exemple en utilisant du code trouvé dans la documentation sur les $transitions. Je suis assez novice avec le routeur d'interface mais cela fonctionne.

J'espère que cela pourra aider quelqu'un.

5voto

Jason Points 21

Voici comment nous sommes sortis de la boucle de routage infinie tout en continuant à utiliser $state.go au lieu de $location.path

if('401' !== toState.name) {
  if (principal.isIdentityResolved()) authorization.authorize();
}

1 votes

Quelqu'un sait-il pourquoi, lorsque l'on utilise la réponse acceptée / la configuration décrite ci-dessus, la barre d'adresse n'affiche plus l'url et tous les fragments et paramètres de la chaîne de requête ? Depuis la mise en place de cette solution, la barre d'adresse ne permet plus à notre application d'être mise en signet.

1 votes

Ne s'agit-il pas d'un commentaire sur l'une des réponses existantes ? Parce qu'il n'y a pas de code de ce type dans l'OP et qu'il n'est même pas clair à quelle réponse/quel code il est fait référence.

3voto

Chris Incoqnito Points 215

J'ai une autre solution : cette solution fonctionne parfaitement lorsque vous n'avez que du contenu que vous voulez afficher lorsque vous êtes connecté. Définissez une règle où vous vérifiez si vous êtes connecté et si 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 si la route actuelle que je veux acheminer ne fait pas partie de `/login', parce que mes routes de la liste blanche sont les suivantes

/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 l'ensemble de 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 une syntaxe ES6, utilisez plutôt function() { /* code */ }

3voto

TSlegaitis Points 395

Utiliser l'intercepteur $http

En utilisant un intercepteur $http, vous pouvez envoyer des en-têtes au back-end ou inversement et effectuer vos vérifications de cette manière.

Excellent article sur 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.

2voto

colefner Points 1293

Tout d'abord, vous aurez besoin d'un service que vous pourrez injecter dans vos contrôleurs et qui aura une idée de l'état de l'authentification de l'application. La persistance des détails d'authentification avec un stockage local est une bonne façon de l'aborder.

Ensuite, vous devrez vérifier l'état de l'authentification juste avant les changements d'état. Puisque votre application a des pages qui ont besoin d'être authentifiées et d'autres qui n'en ont pas besoin, créez une route parent qui vérifie l'authentification, et faites en sorte que toutes les autres pages qui ont besoin de la même chose soient des enfants de cette route parent.

Enfin, vous aurez besoin d'un moyen de savoir si l'utilisateur connecté peut effectuer certaines opérations. Pour ce faire, vous pouvez ajouter une fonction "can" à votre service d'authentification. Can prend deux paramètres : - action - obligatoire - (par exemple 'manage_dashboards' ou 'create_new_dashboard') - objet - optionnel - l'objet sur lequel l'action est effectuée. Par exemple, si vous avez un objet tableau de bord, vous voudrez peut-être vérifier si dashboard.ownerId === loggedInUser.id. (Bien entendu, les informations transmises par le client ne doivent jamais être considérées comme fiables et vous devez toujours les vérifier sur le serveur avant de les enregistrer 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 n'offre aucune garantie.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X