AngularJS: exemple de base pour utiliser l'authentification dans une application à page unique


100

Je suis nouveau sur AngularJS et j'ai parcouru leur tutoriel et j'ai eu une idée de celui-ci.

J'ai un backend pour mon projet prêt où chacun des RESTpoints de terminaison doit être authentifié.

Ce que je veux faire
a.) Je veux avoir une seule page pour mon projet http://myproject.com.
b.) Une fois qu'un utilisateur accède à l'URL dans le navigateur, en fonction du fait que l'utilisateur est connecté ou non, il est présenté avec une page d'accueil / vue ou page de connexion / vue sous la même URL http://myproject.com.
c.) si un utilisateur n'est pas connecté, il remplit le formulaire et le serveur définit une USER_TOKENsession en cours, de sorte que toutes les demandes ultérieures aux points finaux seront authentifiées en fonction deUSER_TOKEN

Mes confusions
a.) Comment puis-je gérer l'authentification côté client à l'aide d'AngularJS? J'ai vu ici et ici mais je n'ai pas compris comment les utiliser
b.) Comment puis-je présenter différentes vues à l'utilisateur en fonction de si l'utilisateur est connecté ou non sous la même URLhttp://myproject.com

J'utilise angular.js pour la toute première fois et je ne sais vraiment pas comment commencer. Tous les conseils et / ou ressources sont très appréciés.


Veuillez consulter l'article ci-dessous frederiknakstad.com
Ajay Beniwal

1
@MichaelCalkins se contenter de placer un lien n'est pas constructif. Vous devriez au moins dire ce que le lien va fournir.
Dave Gordon

Mon b: Contrôle d'accès et authentification AngularJS coderwall.com/p/f6brkg
Michael

L'équipe d'OAuth a une excellente bibliothèque pour ce andreareginato.github.io/oauth-ng
Faktor 10

Réponses:


48

J'ai créé un dépôt github résumant cet article en gros: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

Repo Github ng-login

Plunker

Je vais essayer d'expliquer le mieux possible, j'espère aider certains d'entre vous là-bas:

(1) app.js: Création de constantes d'authentification sur la définition d'application

var loginApp = angular.module('loginApp', ['ui.router', 'ui.bootstrap'])
/*Constants regarding user login defined here*/
.constant('USER_ROLES', {
    all : '*',
    admin : 'admin',
    editor : 'editor',
    guest : 'guest'
}).constant('AUTH_EVENTS', {
    loginSuccess : 'auth-login-success',
    loginFailed : 'auth-login-failed',
    logoutSuccess : 'auth-logout-success',
    sessionTimeout : 'auth-session-timeout',
    notAuthenticated : 'auth-not-authenticated',
    notAuthorized : 'auth-not-authorized'
})

(2) Service Auth: Toutes les fonctions suivantes sont implémentées dans le service auth.js. Le service $ http est utilisé pour communiquer avec le serveur pour les procédures d'authentification. Contient également des fonctions sur l'autorisation, c'est-à-dire si l'utilisateur est autorisé à effectuer une certaine action.

angular.module('loginApp')
.factory('Auth', [ '$http', '$rootScope', '$window', 'Session', 'AUTH_EVENTS', 
function($http, $rootScope, $window, Session, AUTH_EVENTS) {

authService.login() = [...]
authService.isAuthenticated() = [...]
authService.isAuthorized() = [...]
authService.logout() = [...]

return authService;
} ]);

(3) Session: Un singleton pour conserver les données utilisateur. La mise en œuvre ici dépend de vous.

angular.module('loginApp').service('Session', function($rootScope, USER_ROLES) {

    this.create = function(user) {
        this.user = user;
        this.userRole = user.userRole;
    };
    this.destroy = function() {
        this.user = null;
        this.userRole = null;
    };
    return this;
});

(4) Contrôleur parent: considérez cela comme la fonction "principale" de votre application, tous les contrôleurs héritent de ce contrôleur, et c'est l'épine dorsale de l'authentification de cette application.

<body ng-controller="ParentController">
[...]
</body>

(5) Contrôle d'accès: pour refuser l'accès sur certaines routes, 2 étapes doivent être mises en œuvre:

a) Ajoutez les données des rôles autorisés à accéder à chaque route, sur le service $ stateProvider du routeur de l'interface utilisateur, comme on peut le voir ci-dessous (cela peut fonctionner pour ngRoute).

.config(function ($stateProvider, USER_ROLES) {
  $stateProvider.state('dashboard', {
    url: '/dashboard',
    templateUrl: 'dashboard/index.html',
    data: {
      authorizedRoles: [USER_ROLES.admin, USER_ROLES.editor]
    }
  });
})

b) Sur $ rootScope. $ on ('$ stateChangeStart') ajoutez la fonction pour empêcher le changement d'état si l'utilisateur n'est pas autorisé.

$rootScope.$on('$stateChangeStart', function (event, next) {
    var authorizedRoles = next.data.authorizedRoles;
    if (!Auth.isAuthorized(authorizedRoles)) {
      event.preventDefault();
      if (Auth.isAuthenticated()) {
        // user is not allowed
        $rootScope.$broadcast(AUTH_EVENTS.notAuthorized);
      } else {
        // user is not logged in
        $rootScope.$broadcast(AUTH_EVENTS.notAuthenticated);
      }
    }
});

(6) Intercepteur d'authentification: Ceci est implémenté, mais ne peut pas être vérifié sur la portée de ce code. Après chaque requête $ http, cet intercepteur vérifie le code d'état, si l'un des éléments ci-dessous est renvoyé, il diffuse un événement pour forcer l'utilisateur à se reconnecter.

angular.module('loginApp')
.factory('AuthInterceptor', [ '$rootScope', '$q', 'Session', 'AUTH_EVENTS',
function($rootScope, $q, Session, AUTH_EVENTS) {
    return {
        responseError : function(response) {
            $rootScope.$broadcast({
                401 : AUTH_EVENTS.notAuthenticated,
                403 : AUTH_EVENTS.notAuthorized,
                419 : AUTH_EVENTS.sessionTimeout,
                440 : AUTH_EVENTS.sessionTimeout
            }[response.status], response);
            return $q.reject(response);
        }
    };
} ]);

PS Un bogue avec le remplissage automatique des données du formulaire comme indiqué dans le premier article peut être facilement évité en ajoutant la directive qui est incluse dans directives.js.

PS2 Ce code peut être facilement modifié par l'utilisateur, pour permettre de voir différents itinéraires ou d'afficher du contenu qui n'était pas destiné à être affiché. La logique DOIT être implémentée côté serveur, c'est juste un moyen d'afficher correctement les choses sur votre ng-app.


1
J'ai suivi votre guide pour comprendre la logique côté client. C'est vraiment bien!! J'ai manqué quelque chose à propos de la destruction manuelle de sessions, mais nous devons également expérimenter et casser des choses!
Sebastialonso

~~ Je ne sais pas si j'ai bien compris cette ligne: authService.login() = [...]ces crochets représenteront quelque chose comme $http.get(url, {uID, pwd}? ~~ ok, regardé dans le plunker, c'était comme je l'ai dit XD
netalex

1
pouvez-vous élargir votre réponse côté serveur?
requête

25

J'aime l'approche et je l'ai implémentée côté serveur sans rien faire lié à l'authentification sur le front-end

Ma «technique» sur ma dernière application est .. le client ne se soucie pas d'Auth. Chaque élément de l'application nécessite d'abord une connexion, de sorte que le serveur sert toujours une page de connexion à moins qu'un utilisateur existant ne soit détecté dans la session. Si session.user est trouvé, le serveur envoie simplement index.html. Bam: -o

Cherchez le commentaire de "Andrew Joslin".

https://groups.google.com/forum/?fromgroups=#!searchin/angular/authentication/angular/POXLTi_JUgg/VwStpoWCPUQJ


3
si c'est une API Web? Je n'ai pas obtenu votre réponse, je suppose :(
Leandro De Mello Fagundes

1
Et si vous souhaitez afficher le nom d'utilisateur? Ou si vous parlez à un service avec le nom d'utilisateur dans les URL des points de terminaison?
perrygeo

2
désolé, mais je ne comprends pas la réponse. comment gérez-vous la session en angulaire? où est défini session.user? pourriez-vous en faire un exemple de code? merci
François Romain

4
Les sessions sont gérées côté client et non côté serveur, le client enregistre le jeton et l'envoie dans le cadre de chaque requête qu'il fait. Le serveur valide le jeton et traite la demande
daydreamer

4
Quelqu'un qui le comprend pourrait-il modifier cette réponse pour le reste d'entre nous s'il vous plaît?
Alojz Janez

14

J'ai répondu à une question similaire ici: Authentification AngularJS + API RESTful


J'ai écrit un module AngularJS pour UserApp qui prend en charge les routes protégées / publiques, le reroutage lors de la connexion / déconnexion, les pulsations pour les vérifications d'état, stocke le jeton de session dans un cookie, des événements, etc.

Vous pouvez soit:

  1. Modifiez le module et attachez-le à votre propre API, ou
  2. Utilisez le module avec UserApp (une API de gestion des utilisateurs basée sur le cloud)

https://github.com/userapp-io/userapp-angular

Si vous utilisez UserApp, vous n'aurez pas à écrire de code côté serveur pour les éléments de l'utilisateur (plus que la validation d'un jeton). Suivez le cours sur Codecademy pour l'essayer.

Voici quelques exemples de son fonctionnement:

  • Comment spécifier les routes qui doivent être publiques et quelle route est le formulaire de connexion:

    $routeProvider.when('/login', {templateUrl: 'partials/login.html', public: true, login: true});
    $routeProvider.when('/signup', {templateUrl: 'partials/signup.html', public: true});
    $routeProvider.when('/home', {templateUrl: 'partials/home.html'});
    

    L' .otherwise()itinéraire doit être défini vers l'endroit où vous souhaitez que vos utilisateurs soient redirigés après la connexion. Exemple:

    $routeProvider.otherwise({redirectTo: '/home'});

  • Formulaire de connexion avec gestion des erreurs:

    <form ua-login ua-error="error-msg">
        <input name="login" placeholder="Username"><br>
        <input name="password" placeholder="Password" type="password"><br>
        <button type="submit">Log in</button>
        <p id="error-msg"></p>
    </form>
    
  • Formulaire d'inscription avec gestion des erreurs:

    <form ua-signup ua-error="error-msg">
      <input name="first_name" placeholder="Your name"><br>
      <input name="login" ua-is-email placeholder="Email"><br>
      <input name="password" placeholder="Password" type="password"><br>
      <button type="submit">Create account</button>
      <p id="error-msg"></p>
    </form>
    
  • Lien de déconnexion:

    <a href="#" ua-logout>Log Out</a>

    (Termine la session et redirige vers la route de connexion)

  • Accéder aux propriétés utilisateur:

    Les propriétés de l'utilisateur sont accessibles à l'aide du userservice, par exemple:user.current.email

    Ou dans le modèle: <span>{{ user.email }}</span>

  • Masquer les éléments qui ne devraient être visibles que lorsque vous êtes connecté:

    <div ng-show="user.authorized">Welcome {{ user.first_name }}!</div>

  • Afficher un élément basé sur les autorisations:

    <div ua-has-permission="admin">You are an admin</div>

Et pour vous authentifier auprès de vos services back-end, utilisez simplement user.token()pour obtenir le jeton de session et l'envoyer avec la requête AJAX. Au back-end, utilisez l' API UserApp (si vous utilisez UserApp) pour vérifier si le jeton est valide ou non.

Si vous avez besoin d'aide, faites-le moi savoir!


Comment pourrais-je "modifier le module et le joindre à votre propre API" ?
Pureferret

2

Dans angularjs, vous pouvez créer la partie UI, le service, les directives et toute la partie angularjs qui représente l'interface utilisateur. C'est une belle technologie sur laquelle travailler.

Comme toute personne qui découvre cette technologie et souhaite authentifier l '«utilisateur», je suggère de le faire avec la puissance de l'api web c #. pour cela, vous pouvez utiliser la spécification OAuth qui vous aidera à construire un mécanisme de sécurité solide pour authentifier l'utilisateur. une fois que vous avez créé WebApi avec OAuth, vous devez appeler cette API pour le jeton:

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

et une fois que vous obtenez le jeton, vous demandez les ressources à angularjs à l'aide de Token et accédez à la ressource qui a été sécurisée dans l'API Web avec la spécification OAuth.

Veuillez consulter l'article ci-dessous pour plus d'aide: -

http://bitoftech.net/2014/06/09/angularjs-token-authentication-using-asp-net-web-api-2-owin-asp-net-identity/


1

Je pense que chaque réponse JSON doit contenir une propriété (par exemple {authenticated: false}) et que le client doit la tester à chaque fois: si elle est fausse, alors le contrôleur / service Angular "redirigera" vers la page de connexion.

Et que se passe-t-il si l'utilisateur attrape de JSON et change le booléen en True?

Je pense que vous ne devriez jamais compter sur le client pour faire ce genre de choses. Si l'utilisateur n'est pas authentifié, le serveur doit simplement rediriger vers une page de connexion / erreur.


2
Vérifiez ceci: github.com/witoldsz/angular-http-auth - l'intercepteur vérifie le code d'état de réponse du serveur et s'il est 403 (`` connexion requise ''), il diffuse un événement, vous pouvez donc l'attraper dans l'application et afficher la boîte de connexion.
aherok le

10
Arrêtez de vous répondre en utilisant des réponses. C'est à cela que servent les commentaires!
Soviut

Suggestion @aherok, votre commentaire doit être promu en réponse, il sera voté en tête à temps. le reste n'est que du bruit.
user237419

0

var _login = function (loginData) {
 
        var data = "grant_type=password&username=" + loginData.userName + "&password=" + loginData.password;
 
        var deferred = $q.defer();
 
        $http.post(serviceBase + 'token', data, { headers: { 'Content-Type': 'application/x-www-form-urlencoded' } }).success(function (response) {
 
            localStorageService.set('authorizationData', { token: response.access_token, userName: loginData.userName });
 
            _authentication.isAuth = true;
            _authentication.userName = loginData.userName;
 
            deferred.resolve(response);
 
        }).error(function (err, status) {
            _logOut();
            deferred.reject(err);
        });
 
        return deferred.promise;
 
    };
 

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.