JWT Authentication

******* IMPORTANTE!!!! El httpinterceptor se dispara solo si se utiliza la clase HttpClient para invocar al servicio, si se utiliza Http, no se dispara!!! *******

Funcionamiento:

1. El cliente se loguea
2. El servidor genera un token jwt (RS256) encriptándolo con la libreria jwt y lo devuelve en el body
3. El cliente guarda el token en el localStorage
4. En cada invocación, mediante un interceptor, el cliente agrega el token automaticamente en el header Authorization
5. El servidor valida que todo request venga con un token, verificandolo con la libreria jwt
6. Si el token es válido, procesa el request, sino rechaza la solicitud.

Generación de clave privada pública

Para encriptar el token JWT se utiliza RS256.
El token se encripta utilizando la clave privada, y se desencripta utilizando la clave publica.
La clave privada solo sirve para encriptar, y la publica solo sirve para desencriptar.
Se puede generar el par de clave privada y publica RSA mediante este link:
http://travistidwell.com/jsencrypt/demo/

Login:

1. El cliente accede a la pantalla de login, ingresa usuario y contraseña
2. El servidor valida al usuario, y si esta ok
                    -genera un token jwt firmandolo con la clave privada
                    -retorna el token encriptado
3. El cliente almacena el token utilizando localStorage

Incluir automáticamente el token en cada invocación

1. Luego que se logueo exitosamente y se guardo en el localStorage el token, se agrega un httpInterceptor para que en cada request este incluya automáticamente el token en el header Authentication.

Servidor - validar automaticamente el token

1. En el servidor, en app.use(), se verifica mediante jwt que el token recibido en el header Authorization sea válido. Esto se hace mediante jwt.verify. La verificacion se realiza utilizando la clave publica, de las dos que se generaron en el primer paso. Con esta verificacion es suficiente, porque si el token encriptado fue alterado, no sera verificado como valido, ante lo cual se cancela el request.


Servidor:

var express = require("express"), app = express(), appLogin = express(), bodyParser = require("body-parser"), methodOverride = require("method-override"), jwt = require("jsonwebtoken"), fs = require("fs") app.use(function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, content-type, x-access-token, Authorization'); res.setHeader('Access-Control-Allow-Credentials', true); next(); }); app.use(function (req, res, next) { if (req.header('Access-Control-Request-Method') != undefined) return next(); else { const RSA_PUBLIC_KEY = fs.readFileSync('./public.key'); var token = req.header('Authorization'); jwt.verify(token, RSA_PUBLIC_KEY, function(err, decoded) { if (err) { console.log("No autorizado"); res.sendStatus(401); } else { console.log("Autorizado"); next(); } }); } }); app.use(bodyParser.urlencoded({ extended: false })); app.use(bodyParser.json()); app.use(methodOverride()); var router = express.Router(); app.use(router); //LOGIN appLogin.use(bodyParser.urlencoded({ extended: false })); appLogin.use(bodyParser.json()); appLogin.use(methodOverride()); var router2 = express.Router(); appLogin.use(function (req, res, next) { res.setHeader('Access-Control-Allow-Origin', 'http://localhost:4200'); res.setHeader('Access-Control-Allow-Methods', 'GET, POST, OPTIONS, PUT, PATCH, DELETE'); res.setHeader('Access-Control-Allow-Headers', 'X-Requested-With, content-type, x-access-token, Authorization'); res.setHeader('Access-Control-Allow-Credentials', true); next(); }); appLogin.use(router2); router2.post('/login', validarLogin); router.get('/prueba', function(req, res) { var p = [{nombre: 'carlos', apellido: 'russo'}, {nombre: 'karina', apellido: 'ortiz'}]; res.send(p); }); function validarLogin(req,res) { var message; if (req.body.usuario != "crusso") { message="Usuario no existe"; } else { if (req.body.password != "abc123") { message="Password incorrecto"; } else{ message="ok"; } } if (message != "ok") res.send(JSON.stringify(message)); else { const RSA_PRIVATE_KEY = fs.readFileSync('./private.key'); const token = jwt.sign({}, RSA_PRIVATE_KEY, { algorithm: 'RS256', expiresIn: 120, subject: req.body.usuario }); console.log(token); res.send(JSON.stringify({'token' : token})); } } app.listen(8080, function() { console.log("Node server running on http://localhost:8080"); }); appLogin.listen(8081, function() { console.log("Node server running on http://localhost:8081"); });

Cliente:

app.component.html

<form (ngSubmit)="onSubmit()" method="POST">
Usuario: <input type="text" [(ngModel)]="usuario" name="usuario">
<br>
Password:<input type="password" [(ngModel)]="password" name="password">
<br>
<input type="submit" value="Enviar" />
</form> 


app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import { AuthenticationService } from './_services/authentication.service';
import { Http } from '@angular/http';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})

export class AppComponent {
  usuario: string;
  password: string;

  ngOnInit() {
    this.usuario = "crusso";
    this.password = "abc123";
  }

  title = 'app';
  constructor(private httpClient: HttpClient, private authenticationService : AuthenticationService) { }

  metodo1(): void { 
      this.httpClient.get("http://localhost:8080/prueba").subscribe((datos) => {               
          console.log(datos);   
       });     
  }

  onSubmit() {
    console.log(this.usuario + " " + this.password);
 
    this.authenticationService.login(this.usuario, this.password).subscribe((datos) => {               
      console.log(datos);   
   });
  } 

  logout() {
    console.log("logout");
    this.authenticationService.logout();
  }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
import { InterceptorHttp } from './_interceptor/interceptorhttp'
import { AppComponent } from './app.component';
import { AuthenticationService } from './_services/authentication.service';
import { FormsModule } from '@angular/forms';
import { RouterModule } from '@angular/router';
import { HttpModule } from '@angular/http';
import { RouterOutlet } from '@angular/router';

@NgModule({
declarations: [
  AppComponent,
],
imports: [
  BrowserModule,
  HttpClientModule,
  FormsModule,
  RouterModule,
  HttpModule,
  HttpClientModule,
  //RouterModule.forRoot([ {path: 'usuarios', component: PruebaComponent} ])
],
providers: [
  {
    provide: HTTP_INTERCEPTORS,
    useClass: InterceptorHttp,
    multi: true,
  },
  AuthenticationService
],
bootstrap: [AppComponent]
})

export class AppModule { }

interceptorhttp.ts

import {Injectable} from "@angular/core";
import {HttpEvent, HttpHandler, HttpInterceptor} from "@angular/common/http";
import {HttpRequest} from "@angular/common/http";
import {Observable} from "rxjs/Observable";

@Injectable()
export class InterceptorHttp implements HttpInterceptor {
    constructor() {}

    intercept(req: HttpRequest<any>, next: HttpHandler):Observable<HttpEvent<any>> {
        var token = '';

        if (localStorage.getItem('currentUser') != null)
        {
            var t = JSON.parse(localStorage.getItem('currentUser'));
            token = t.token;
        }
        else
            token = '';

        const customHeaderRequest = req.clone({
            headers: req.headers.set('Authorization', token)
        });     

        console.log("INTERCEPTANDO: " + token);
        return next.handle(customHeaderRequest);
    }
}

authentication.service.ts

import { Injectable } from '@angular/core';
import { Http, Headers, Response, RequestOptions } from '@angular/http';
import { Observable } from 'rxjs';
import 'rxjs/add/operator/map'

@Injectable()
export class AuthenticationService {

  constructor(private http: Http) {       
      var currentUser = JSON.parse(localStorage.getItem('currentUser'));
      console.log("CONSTRUCTOR: " + currentUser);
  }

  login(usuario: string, password: string): Observable<boolean> {     

    return this.http.post('http://localhost:8081/login', { usuario: usuario, password: password })     
        .map((response: Response) => {         
            let token = response.json() && response.json().token;
                 
            if (token) {                 
                localStorage.setItem('currentUser', JSON.stringify(response.json()));

                return true;
            } else {               
                return false;
            }                 
        });
  }

  logout(): boolean {       
      localStorage.removeItem('currentUser');

      return true;
  }
}


Fuente: https://blog.angular-university.io/angular-jwt-authentication/

No hay comentarios:

Publicar un comentario