• Home
  • Angular
  • Angular Authentication: Usando Http Client y Http Interceptors

Angular Authentication: Usando Http Client y Http Interceptors

Autenticación en Angular

Hola buenas a todos, autenticar nuestra aplicación en angular es algo que tenemos que hacer desde el principio, hoy vamos a ver este tema para tener una app segura.

Autenticación en Angular 6

Desde hace un tiempo se agrego varias herramientas para nuestras peticiones HTTP con un monton de características muy útiles. Tal vez la característica más largamente esperada es la interfaz HttpInterceptor. Hasta hace un tiempo, no había forma de interceptar y modificar solicitudes HTTP de forma global. Esto siempre ha sido posible en AngularJS y el hecho de que haya faltado en Angular 2+ ha sido un punto de fricción con los desarrolladores.

Entonces, ¿por qué son útiles los interceptores HTTP? Hay muchas razones, pero un caso de uso común es adjuntar automáticamente información de autenticación a las solicitudes. Esto puede tomar varias formas diferentes, pero la mayoría de las veces implica la incorporación de un token web JSON (u otra forma de token de acceso) como un AuthorizationHeader con el esquema Bearer.

Echemos una mirada a cómo utilizar la interfaz HttpInterceptor de Angular para realizar solicitudes HTTP autenticadas.

Crear un servicio de autenticación

Al manejar la autenticación en una aplicación angular, generalmente es mejor colocar todo lo que necesita en un servicio dedicado. Cualquier servicio de autenticación debe tener unos pocos métodos básicos para permitir a los usuarios iniciar y cerrar sesión. También debe incluir un método para recuperar un JSON Web Token desde donde esté almacenado en el cliente y una forma de determinar si el usuario está autenticado o no.

import { Injectable } from '@angular/core';
import { JhiLanguageService } from 'ng-jhipster';

import { Principal } from '../auth/principal.service';
import { AuthServerProvider } from '../auth/auth-jwt.service';

@Injectable()
export class LoginService {

    constructor(
        private languageService: JhiLanguageService,
        private principal: Principal,
        private authServerProvider: AuthServerProvider
    ) {}

    login(credentials, callback?) {
        const cb = callback || function() {};

        return new Promise((resolve, reject) => {
            this.authServerProvider.login(credentials).subscribe((data) => {
                this.principal.identity(true).then((account) => {
                    // After the login the language will be changed to
                    // the language selected by the user during his registration
                    if (account !== null) {
                        this.languageService.changeLanguage(account.langKey);
                    }
                    resolve(data);
                });
                return cb();
            }, (err) => {
                this.logout();
                reject(err);
                return cb(err);
            });
        });
    }

    loginWithToken(jwt, rememberMe) {
        return this.authServerProvider.loginWithToken(jwt, rememberMe);
    }

    logout() {
        this.authServerProvider.logout().subscribe();
        this.principal.authenticate(null);
    }
}
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs/Observable';
import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
import { SERVER_API_URL } from '../../app.constants';

@Injectable()
export class AuthServerProvider {
    constructor(
        private http: HttpClient,
        private $localStorage: LocalStorageService,
        private $sessionStorage: SessionStorageService
    ) {}

    getToken() {

        return this.$localStorage.retrieve('authenticationToken') || this.$sessionStorage.retrieve('authenticationToken');
    }

    login(credentials): Observable<any> {

        const data = {
            username: credentials.username,
            password: credentials.password,
            rememberMe: credentials.rememberMe
        };
        return this.http.post(SERVER_API_URL + 'api/authenticate', data, {observe : 'response'}).map(authenticateSuccess.bind(this));

        function authenticateSuccess(resp) {
            const bearerToken = resp.headers.get('Authorization');
            if (bearerToken && bearerToken.slice(0, 7) === 'Bearer ') {
                const jwt = bearerToken.slice(7, bearerToken.length);
                this.storeAuthenticationToken(jwt, credentials.rememberMe);
                return jwt;
            }
        }
    }

    loginWithToken(jwt, rememberMe) {
        if (jwt) {
            this.storeAuthenticationToken(jwt, rememberMe);
            return Promise.resolve(jwt);
        } else {
            return Promise.reject('auth-jwt-service Promise reject'); // Put appropriate error message here
        }
    }

    storeAuthenticationToken(jwt, rememberMe) {
        if (rememberMe) {
            this.$localStorage.store('authenticationToken', jwt);
        } else {
            this.$sessionStorage.store('authenticationToken', jwt);
        }
    }

    logout(): Observable<any> {

        return new Observable((observer) => {
            this.$localStorage.clear('authenticationToken');
            this.$sessionStorage.clear('authenticationToken');
            observer.complete();
        });
    }
}

 

Crear un interceptor

El objetivo es incluir el JWT que está en el almacenamiento local como AuthorizationenHeader en cualquier solicitud HTTP que se envíe. El primer paso es crear un interceptor. Para hacer esto, crea una clase Injectable que implemente HttpInterceptor.

import { Observable } from 'rxjs/Observable';
import { LocalStorageService, SessionStorageService } from 'ngx-webstorage';
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
import { SERVER_API_URL } from '../../app.constants';

export class AuthInterceptor implements HttpInterceptor {

    constructor(
        private localStorage: LocalStorageService,
        private sessionStorage: SessionStorageService
    ) {
    }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        if (!request || !request.url || (/^http/.test(request.url) && !(SERVER_API_URL && request.url.startsWith(SERVER_API_URL)))) {
            return next.handle(request);
        }

        const token = this.localStorage.retrieve('authenticationToken') || this.sessionStorage.retrieve('authenticationToken');
        if (!!token) {
            request = request.clone({
                setHeaders: {
                    Authorization: 'Bearer ' + token
                }
            });
        }
        return next.handle(request);
    }

}

Cualquier interceptor que queremos crear necesita implementar la interfaz HttpInterceptor. Esto significa que nuestra nueva clase debe tener un método llamado intercept con los parametros HttpRequest y HttpHandler. El uso de interceptores se trata de cambiar las solicitudes salientes y las respuestas entrantes, pero no podemos alterar la solicitud original: debe ser inmutable. Para hacer cambios, necesitamos clonar el request original.

Al clonar el request original, podemos establecer los encabezados que queremos. En nuestro caso es muy simple: solo queremos agregar un Authorization Header con un esquema de autenticación Bearer seguido del JSON Web Token obtenido mediante localstorage y sessionStorage, mirar codigo propuesto.

Llamar next.handle significa que estamos pasando el control al siguiente interceptor en la cadena, si hay uno.

Agregue el interceptor a los proveedores

El interceptor necesita ser agregado a la matriz HTTP_INTERCEPTORS. Esto se hace haciendo que la matriz HTTP_INTERCEPTORS existente use la nueva clase que hemos creado. Agregue esto en el provider del módulo principal de nuestra aplicación.

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppComponent } from './app.component';
import {AppRoutingModule} from './routing/app-routing';
import {HTTP_INTERCEPTORS, HttpClientModule} from '@angular/common/http';
import {VistaModule} from './vista/vista.module';
import {LayoutModule} from './layout/layout.module';
import {AuthInterceptor} from './blocks/interceptor/auth.interceptor';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
      BrowserModule,
      AppRoutingModule,
      HttpClientModule,
      VistaModule,
      LayoutModule
  ],
  providers: [
    {
      provide: HTTP_INTERCEPTORS,
      useClass: AuthInterceptor,
      multi: true
    }
  ],
  bootstrap: [AppComponent]
})
export class AppModule { }

Ahora cuando hacemos una solicitud HTTP, el token del usuario se adjuntará automáticamente.

public ping() {
    this.http.get('https://example.com/api/things')
      .subscribe(
        data => console.log(data),
        err => console.log(err)
      );
  }

Esta solicitud incluirá un Authorization al encabezado de la peticion con un valor  Bearer token...

Cabe señalar que el nuevo HttpClient de Angular @angular/common/http se está utilizando aquí y no la clase Http de @angular/http. Si tratamos de hacer solicitudes con la clase Http tradicional , el interceptor no será golpeado.

Al hacer todo esto nos estamos asegurando de que cualquier petición que se ejecute desde la aplicación pase antes por el interceptor, este verifica que en la session o en el localstorage este el token guardado, en el caso de que no este el token la peticion sera denegada, ya que no se adjunta el token a la peticion y tendriamos un error.

Espero les sea de ayuda esto, así como ami me fue de ayuda.

LEAVE YOUR COMMENTS