AngularJS: ejemplo básico para usar la autenticación en una aplicación de página única

100

Soy nuevo en AngularJS y revisé su tutorial y lo sentí.

Tengo un backend para mi proyecto listo donde cada uno de los RESTpuntos finales necesita ser autenticado.

Lo que quiero hacer
a.) Quiero tener una sola página para mi proyecto http://myproject.com.
b.) Una vez que un usuario ingresa a la URL en el navegador, según si el usuario ha iniciado sesión o no, se le presenta una página / vista de inicio o una página / vista de inicio de sesión con la misma URL http://myproject.com.
c.) si un usuario no ha iniciado sesión, completa el formulario y el servidor establece una USER_TOKENsesión, por lo que todas las solicitudes posteriores a los puntos finales se autenticarán segúnUSER_TOKEN

Mis Confusiones
a.) ¿Cómo puedo manejar la autenticación del lado del cliente usando AngularJS? Vi aquí y aquí, pero no entendí cómo usarlos
b.) ¿Cómo puedo presentar diferentes vistas al usuario en función de si el usuario está conectado o no con la misma URL?http://myproject.com

Estoy usando angular.js por primera vez y realmente me confundo sobre cómo comenzar. Cualquier consejo y / o recurso son muy apreciados.

soñador
fuente
Por favor, eche un vistazo al artículo siguiente frederiknakstad.com/…
Ajay Beniwal
1
@MichaelCalkins simplemente colocar un enlace no es constructivo. Al menos debería decir lo que proporcionará el enlace.
Dave Gordon
My b: control de acceso y autenticación de AngularJS coderwall.com/p/f6brkg
Michael J. Calkins
El equipo de OAuth tiene una gran biblioteca para esto andreareginato.github.io/oauth-ng
Faktor 10 de

Respuestas:

48

He creado un repositorio de github que resume este artículo básicamente: https://medium.com/opinionated-angularjs/techniques-for-authentication-in-angularjs-applications-7bbf0346acec

ng-login repositorio de Github

Plunker

Intentaré explicarlo lo mejor posible, espero ayudar a algunos de ustedes:

(1) app.js: creación de constantes de autenticación en la definición de la aplicación

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) Servicio de autenticación: todas las funciones siguientes se implementan en el servicio de autenticación. El servicio $ http se utiliza para comunicarse con el servidor para los procedimientos de autenticación. También contiene funciones de autorización, es decir, si el usuario puede realizar una determinada acción.

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) Sesión: Un singleton para mantener los datos del usuario. La implementación aquí depende de ti.

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) Controlador principal: considere esto como la función "principal" de su aplicación, todos los controladores heredan de este controlador y es la columna vertebral de la autenticación de esta aplicación.

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

(5) Control de acceso: Para denegar el acceso en determinadas rutas, se deben implementar 2 pasos:

a) Agregue datos de los roles permitidos para acceder a cada ruta, en el servicio $ stateProvider del enrutador ui como se puede ver a continuación (lo mismo puede funcionar para ngRoute).

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

b) En $ rootScope. $ on ('$ stateChangeStart') agregue la función para evitar el cambio de estado si el usuario no está autorizado.

$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) Interceptor de autenticación: esto está implementado, pero no se puede verificar en el alcance de este código. Después de cada solicitud $ http, este interceptor comprueba el código de estado, si se devuelve uno de los siguientes, emite un evento para obligar al usuario a iniciar sesión nuevamente.

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

PD: Un error con el autocompletado de datos del formulario como se indica en el primer artículo puede evitarse fácilmente agregando la directiva que se incluye en directives.js.

PS2 El usuario puede modificar este código fácilmente para permitir que se vean diferentes rutas o mostrar contenido que no estaba destinado a mostrarse. La lógica DEBE implementarse en el lado del servidor, esta es solo una forma de mostrar las cosas correctamente en su ng-app.

Alex Arvanitidis
fuente
1
He estado siguiendo su guía para comprender la lógica del lado del cliente. ¡¡Es muy bueno!! Me perdí algo sobre la destrucción manual de sesiones, ¡pero también tenemos que experimentar y romper cosas!
Sebastialonso
~~ no estoy seguro de haber entendido correctamente esa línea: ¿ authService.login() = [...]esos corchetes representarán algo como $http.get(url, {uID, pwd}? ~~ ok, miré en el plunker, fue como dije XD
netalex
1
¿Puede ampliar su respuesta para el lado del servidor?
consulta
25

Me gusta el enfoque y lo implementé en el lado del servidor sin hacer nada relacionado con la autenticación en el front-end

Mi 'técnica' en mi última aplicación es ... al cliente no le importa la autenticación. Cada cosa en la aplicación requiere un inicio de sesión primero, por lo que el servidor siempre ofrece una página de inicio de sesión a menos que se detecte un usuario existente en la sesión. Si se encuentra session.user, el servidor simplemente envía index.html. Bam: -o

Busque el comentario de "Andrew Joslin".

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

soñador
fuente
3
si es una API web? Supongo que no obtuve tu respuesta :(
Leandro De Mello Fagundes
1
¿Qué sucede si desea mostrar el nombre de usuario? ¿O si está hablando con un servicio con el nombre de usuario en las URL del punto final?
perrygeo
2
lo siento, pero no entiendo la respuesta. ¿Cómo manejas la sesión en angular? ¿Dónde está configurado session.user? ¿Podría hacer un ejemplo de código de esto por favor? gracias
François Romain
4
Las sesiones se manejan en el lado del cliente y no en el servidor, el cliente guarda el token y lo envía como parte de cada solicitud que realiza. El servidor valida el token y procesa la solicitud
daydreamer
4
¿Podría alguien que lo entienda editar esta respuesta para el resto de nosotros, por favor?
Alojz Janez
14

Respondí una pregunta similar aquí: Autenticación AngularJS + API RESTful


Escribí un módulo AngularJS para UserApp que admite rutas protegidas / públicas, redireccionamiento al iniciar sesión / cerrar sesión, latidos para verificaciones de estado, almacena el token de sesión en una cookie, eventos, etc.

Podrías:

  1. Modifique el módulo y adjúntelo a su propia API, o
  2. Utilice el módulo junto con UserApp (una API de gestión de usuarios basada en la nube)

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

Si usa UserApp, no tendrá que escribir ningún código del lado del servidor para las cosas del usuario (más que validar un token). Realice el curso de Codecademy para probarlo.

A continuación, se muestran algunos ejemplos de cómo funciona:

  • Cómo especificar qué rutas deben ser públicas y qué ruta es el formulario de inicio de sesión:

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

    La .otherwise()ruta debe establecerse a donde desea que sus usuarios sean redirigidos después de iniciar sesión. Ejemplo:

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

  • Formulario de inicio de sesión con manejo de errores:

    <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>
  • Formulario de registro con manejo de errores:

    <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>
  • Enlace de cierre de sesión:

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

    (Finaliza la sesión y redirige a la ruta de inicio de sesión)

  • Acceder a las propiedades del usuario:

    Se accede a las propiedades del usuario mediante el userservicio, por ejemplo:user.current.email

    O en la plantilla: <span>{{ user.email }}</span>

  • Ocultar elementos que solo deberían estar visibles cuando inicie sesión:

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

  • Muestre un elemento basado en permisos:

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

Y para autenticarse en sus servicios de back-end, solo use user.token()para obtener el token de sesión y enviarlo con la solicitud AJAX. En el back-end, use la API de UserApp (si usa UserApp) para verificar si el token es válido o no.

Si necesita ayuda, ¡hágamelo saber!

Timothy E. Johansson
fuente
¿Cómo puedo "modificar el módulo y adjuntarlo a su propia API" ?
Pureferret
2

En angularjs puede crear la parte de la interfaz de usuario, el servicio, las directivas y toda la parte de angularjs que representa la interfaz de usuario. Es una buena tecnología para trabajar.

Como cualquiera que sea nuevo en esta tecnología y quiera autenticar al "Usuario", sugiero hacerlo con el poder de c # web api. para eso, puede usar la especificación OAuth que lo ayudará a construir un mecanismo de seguridad sólido para autenticar al usuario. una vez que construya WebApi con OAuth, debe llamar a esa api para token:

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

y una vez que obtiene el token, solicita los recursos de angularjs con la ayuda de Token y accede al recurso que se mantuvo seguro en la API web con la especificación OAuth.

Por favor, eche un vistazo al siguiente artículo para obtener más ayuda: -

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

Gurupreet
fuente
1

Creo que cada respuesta JSON debe contener una propiedad (por ejemplo, {autenticado: falso}) y el cliente tiene que probarlo cada vez: si es falso, entonces el controlador / servicio angular "redirigirá" a la página de inicio de sesión.

¿Y qué sucede si el usuario detecta JSON y cambia el bool a True?

Creo que nunca debes confiar en el lado del cliente para hacer este tipo de cosas. Si el usuario no está autenticado, el servidor debería redirigirlo a una página de inicio de sesión / error.

Doum
fuente
2
Verifique esto: github.com/witoldsz/angular-http-auth : el interceptor verifica el código de estado de respuesta del servidor y si es 403 ('se requiere inicio de sesión'), transmite un evento, por lo que puede detectarlo dentro de la aplicación y mostrar el cuadro de inicio de sesión.
aherok
10
Deja de responderte el uno al otro usando respuestas. ¡Para eso están los comentarios!
Soviut
Sugerencia de @aherok, su comentario debe promoverse a una respuesta, se votará a tiempo. el resto es solo ruido.
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;
 
    };
 

Nver Abgaryan
fuente