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/

IIS ASP.NET AutoStart

La primera vez que un usuario accede a una aplicacion, esta es compilada, lo cual produce una demora.
Para evitarlo, se puede configurar el modo AutoStart en IIS

En el application pool:

click derecho / configuracion avanzada:
        start mode: always running

En el sitio web:

click derecho / administrar aplicacion / configuracion avanzada:
        preload enabled: true


Authorization token ANGULAR NODEJS con Interceptor http

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

Servidor

app.js

var express = require("express"),
    app = express(),
    bodyParser  = require("body-parser"),
    methodOverride = require("method-override");

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

    if (req.header('Authorization') == undefined)
        return next();
     
    if (req.header('Authorization') == "Bearer #112222")
    {
        console.log("Autorizado");
        next();
    } else
    {
        console.log("NO autorizado");             
        res.sendStatus(401);
    }     
});

app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
app.use(methodOverride());

var router = express.Router();

app.use(router);

router.get('/prueba', function(req, res) {
    var p = [{nombre: 'carlos', apellido: 'russo'},
             {nombre: 'karina', apellido: 'ortiz'}];

    res.send(p);
});

app.listen(8080, function() {
  console.log("Node server running on http://localhost:8080");
});


Cliente

app.component.html

<div style="text-align:center">
<h1>Prueba de authentication con token</h1>

<button (click)="metodo1()">Method 1</button>
</div>

app.component.ts

import { Component } from '@angular/core';
import { HttpClient } from "@angular/common/http";

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

export class AppComponent {

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

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


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 './interceptorhttp'
import { AppComponent } from './app.component';

@NgModule({
declarations: [
  AppComponent,
],
imports: [
  BrowserModule,
  HttpClientModule
],
providers: [
{
  provide: HTTP_INTERCEPTORS,
  useClass: InterceptorHttp,
  multi: true
}
],
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";
//import {Http, Headers, RequestOptions } from '@angular/http';

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

    intercept(req: HttpRequest<any>,
               next: HttpHandler):Observable<HttpEvent<any>> {
        console.log("interceptando!!!");

        const customHeaderRequest = req.clone({
            headers: req.headers.set('Authorization', 'Bearer #112222')
        });
     
        console.log(req);

       return next.handle(customHeaderRequest);
    }
}





Angular - Failed to load xxxxx: Response for preflight has invalid HTTP status code 404

Si es un llamado POST, verificar que la url no tenga los parametros tipo GET

ej

en lugar de: router.post('http://localhost:8080/competidores/guardar/:competidor', function(req, res)

debe ser: router.post('http://localhost:8080/competidores/guardar', function(req, res)

y los parametros se obtienen con req.body

Angular deploy to Heroku

//solo 1ra vez
heroku git:clone -a gastos-2020

//para subir cambios
git add .
git commit -am "make it better"
git push heroku master

Pasar valores de un listbox a otro con Angular


app.component.ts

import { Component, OnInit } from '@angular/core';
import { NgForm } from '@angular/forms';

@Component({
  selector: 'app-filtroavanzado',
  templateUrl: './filtroavanzado.component.html',
  styleUrls: ['./filtroavanzado.component.css']
})
export class FiltroavanzadoComponent implements OnInit {
  sucursales = [];
  sucursales_sel = [];
  sel_origen = [];
  sel_destino = [];

  constructor() { }

  ngOnInit() {
    this.sucursales.push({name: 'Maria', value: 1});
    this.sucursales.push({name: 'Juan', value: 2});
    this.sucursales.push({name: 'Pedro', value: 3});
    this.sucursales.push({name: 'Arturo', value: 4});
    this.sucursales.push({name: 'Fernando', value: 5});
    this.sucursales.push({name: 'Diego', value: 6});
  }

  agregar() {   
      this.sel_origen.forEach(item => {       
        let server = this.sucursales.find(x => x.value === parseInt(item));

        let elem = this.sucursales_sel.find(x => x.value === parseInt(server.value));

        if (elem == undefined)
           this.sucursales_sel.push(server);                 
      });
  }

  quitar() {     
    this.sel_destino.forEach(item => {
      let x = this.sucursales_sel.find(x => x.value);
      this.sucursales_sel.splice(x, 1);
    })
  } 
}

interface Item {
  name: string;
  value: number;
}


app.component.html

<div>
  <div>
    <select [(ngModel)]="sel_origen" name="origen" multiple="multiple">
      <option *ngFor="let x of sucursales" value={{x.value}}>{{x.name}}</option> 
    </select>
  </div>
  <div> 
      <button (click)='agregar()'>agregar</button>
      <button (click)='quitar()'>quitar</button>
  </div>

  <select [(ngModel)]="sel_destino" name="destino" multiple="multiple">
      <option *ngFor="let x of sucursales_sel" value={{x.value}}>{{x.name}}</option> 
  </select> 
</div>


Angular - referencia rápida


Dropdown (con datos fijos)

<select [(ngModel)]='servidor' id='servidor' name='servidor'>
       <option value='Desarrollo'>Desarrollo</option>
       <option value='Testing'>Testing</option>
       <option value='Produccion'>Produccion</option>
</select>


Dropdown (con datos del componente)

html

<select>
  <option *ngFor="let x of prueba" value={{x.value}}>{{x.nombre}}</option>
</select>

componente

import { Component } from '@angular/core';

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

export class AppComponent {
  prueba = [{nombre: 'abc', value: 1},
           {nombre: 'dddd', value: 2}];
}

Lista con datos del componente

<ul>
  <li *ngFor="let d of datos">{{ d.nombre }}</li>
</ul>

Boton con routerlink y parametros

en app.module.ts agregar: 
import { RouterModule } from '@angular/router';

imports: [RouterModule]

y agregar las rutas en la seccion imports:

RouterModule.forRoot([ {path: 'busqueda/:parametro1/:parametro2', component: PruebaComponent} ])


y en el html, definir el link y agregar el router-outlet donde se desplegara el contenido del componente que se invoque con el routerLink

<button [routerLink]="['/busqueda', parametro1, parametro2]">Buscar</button>

<router-outlet></router-outlet>



Invocar a un metodo del componente

<button (click)="valores()">Valores</button>

Utilizar ngModel para two binding

en app.module.ts agregar:

import { FormsModule } from '@angular/forms';
imports: [
    FormsModule
  ],

para utilizarlo: (es importante que tenga el name!!!!

<input [(ngModel)]='telefono' name='telefono' type="text" size="10" />

Servicios

cd <aplicacion>\src\app
mkdir services
cd services
ng generate service datos (el nombre datos cambiarlo por el nombre del servicio)

en datos.service.ts:

import { Http, Response } from '@angular/http';
import 'rxjs/add/operator/map';

en el constructor, agregar:

constructor(public http: Http) { }

  lista(param1: string, param2: string) {         
    return this.http.get("http://localhost:8080/lista/" + param1 + "/" + param2)     
    .map(res => res.json());       
  }

en app.module.ts:

import { DatosService } from './datos.service';

y en providers:

providers: [DatosService]

En el componente que lo va a invocar (ej: consulta.component.ts)

import { DatosService } from './datos.service';

constructor(private data : DatosService)

v: any;

lista(param1: string, param2: string) {                
   this.data.lista(param1, param2).subscribe((datos) => {      
      this.v = datos;    

      console.log(datos);      
   });
}

Ocultar mediante ngIf

<form *ngIf="activo" (ngSubmit)="onSubmit()" method="POST">
  Usuario: <input type="text" [(ngModel)]="name" name="name">
  <br>
Password:<input type="text" [(ngModel)]="password" name="password">
  <br>

<input type="submit" value="Enviar" />
</form>

(En el modulo.ts, agregar una atributo que se llame activo y ponerlo en true o false para probar)


@Output y @Input : intercambiar datos entre los componentes

@Input: para pasar un dato de un componente parent a un child:

En este caso el componente padre se llama "parent" y el hijo se llama "child"
El dato se establece en el componente "parent", y se recibe como @Input en el componente "child"

Ej:

child.component.ts

import { Component, OnInit, Output, Input } from '@angular/core';

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

export class ChildComponent implements OnInit {
  @Input() myAwesomeValue : string;

  constructor() { }

  ngOnInit() { 
  }
}

parent.component.html

<app-child [myAwesomeValue]="dato"></app-child>


parent.component.ts

import { Input, Component, OnInit } from '@angular/core';

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

export class ParentComponent {
  constructor() { }

  @Input() dato: string;

  ngOnInit() {
    this.dato = "EL DATO!!!!";
  }
}






Diferentes rutas para el mismo componente en Angular


you can solve it by adding routes
const routes: Routes = [
    { path: 'experience',
        children: [
            { path: 'pending', component: ExperienceComponent },
            { path: 'requests', component: ExperienceComponent },
        ] }]
and in ExperienceComponent import
import { ActivatedRoute } from "@angular/router";
and in constructor add this parameter
constructor(public route: ActivatedRoute)
and inside constructor get url
this.route.url.subscribe(params => {
  console.log(params[0].path);
})

Parametros POST en NodeJS y Angular


Ejemplo 1 (Con formulario):

Cliente:

app.component.html

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

<input type="submit" value="Enviar" />
</form>

app.component.ts

import { Component } from '@angular/core';
import { DataService } from './data.service';
import { Http, Response } from '@angular/http';
import 'rxjs/add/operator/map';

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

export class AppComponent {
  constructor(private data : DataService) {}

  title = 'app';
  name: string;
  password: string;

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


app.module.ts

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

import { AppComponent } from './app.component';
import { DataService } from './data.service';

import { HttpModule } from '@angular/http';

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [DataService],
  bootstrap: [AppComponent]
})
export class AppModule { }


data.service.ts

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

@Injectable()
export class DataService {

  constructor(public http: Http) { }

  login(name: string, password: string) {
    console.log("llamando al servicio");       
 
    return this.http.post("http://localhost:3000/login", {name: name, password: password})     
    .map(res => res.json());       
  } 
}

Servidor

var express=require('express');
var app=express();
var bodyParser = require('body-parser');

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');
    res.setHeader('Access-Control-Allow-Credentials', true);
    next();
});

var router=express.Router();
router.use(bodyParser.json());
router.use(bodyParser.urlencoded({extended: false}));

router.get('/', function(req, res) {
    res.send("Hello World!");
 });
router.post('/login',testFunction);

router.get('/prueba', function(req, res) {
    console.log("prueba...");

    res.send(JSON.stringify("ok"));
});

function testFunction(req,res) { 
    var name=req.body.name;
    var password=req.body.password;
 
    console.log("Name:" + name + " Password:" + password);

    res.send(JSON.stringify("ok"));
}

app.use(router);

//module.exports=router;

app.listen(3000, function() {
    console.log("Node server running on http://localhost:3000");
});




Ejemplo 2:


router.post('/competidores/guardar', function (req, res) {
    var nombre = req.body.nombre;
    var direccion = req.body.direccion;
 
    console.log(nombre);

    res.send(nombre);
});


Para testearlo con Postman:
Elegir POST
Luego Body
x-www-form-urlencoded


Ejemplo con insert:

router.post('/competidores/guardar', function (req, res) {
    var body = req.body;
    var resultado = '';

    con.query(
        'INSERT INTO competidor (numero, nombres, apellidos, cedula, apodo, barrio, nacionalidad, direccion, telefono, ' +
               'celular, email, estatura, experiencia, fecha_nacimiento, gimnasio, entrenador, sociedad, peso) ' +
            'VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
            [body.numero, body.nombres, body.apellidos, body.cedula, body.apodo, body.barrio, body.nacionalidad,
             body.direccion, body.telefono, body.celular, body.email, body.estatura, body.experiencia,
             body.fecha_nacimiento, body.gimnasio, body.entrenador, body.sociedad, body.peso],
        function (err, results) { 
            if (err == null)
                resultado = "OK";
            else
                resultado = err;

            console.log(resultado);
        }
    );   

    res.send(resultado);     
});

WebApi - Angular - ejemplo completo con CORS, Authorization, rutas con nombre y JSON [** 100% FUNCIONANDO **]

Instalar con NuGet console:

Install-Package Microsoft.AspNet.WebApi.Cors


WebApiConfig.cs

public static void Register(HttpConfiguration config)
{

 //configuracion para que retorne JSON en lugar de XML
config.Formatters.JsonFormatter.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/html"));

 //habilita CORS para cualquier request
var cors = new EnableCorsAttribute("*", "*", "*");
config.EnableCors();

 //permite indicar nombres especificos en las rutas REST, en lugar de los estandares (get, post, put, etc)
config.MapHttpAttributeRoutes();

config.Routes.MapHttpRoute(
name: "ActionApi",
routeTemplate: "api/{controller}/{action}/{id}",
defaults: new { id = RouteParameter.Optional }
);

config.MessageHandlers.Add(new TokenValidationHandler());

 //indica que el filtro debe aplicarse a todos los elementos del WebApi (intercepta todos los requests)
config.Filters.Add(new AuthorizeAttribute());
}

LoginController.cs

public class LoginController : ApiController
{
//ej de uso: localhost/servicios/api/login/authenticate
[HttpPost]
[AllowAnonymous]      //esto permite evitar que se aplique el filtro, dado que todavia no tenemos el token
public IHttpActionResult Authenticate([FromBody] LoginRequest login)
{
var loginResponse = new LoginResponse { };
LoginRequest loginrequest = new LoginRequest { };
loginrequest.Username = login.Username.ToLower();
loginrequest.Password = login.Password;

IHttpActionResult response;
HttpResponseMessage responseMsg = new HttpResponseMessage();
bool isUsernamePasswordValid = false;

if (login != null)
isUsernamePasswordValid = loginrequest.Password == "admin" ? true : false;
// if credentials are valid
if (isUsernamePasswordValid)
{
string token = createToken(loginrequest.Username);
//return the token
return Ok<string>(token);
}
else
{
// if credentials are not valid send unauthorized status code in response
loginResponse.responseMsg.StatusCode = HttpStatusCode.Unauthorized;
response = ResponseMessage(loginResponse.responseMsg);
return response;
}
}

private string createToken(string username)
{
//Set issued at date
DateTime issuedAt = DateTime.UtcNow;
//set the time when it expires
DateTime expires = DateTime.UtcNow.AddDays(7);

//http://stackoverflow.com/questions/18223868/how-to-encrypt-jwt-security-token
var tokenHandler = new JwtSecurityTokenHandler();

//create a identity and add claims to the user which we want to log in
ClaimsIdentity claimsIdentity = new ClaimsIdentity(new[]
{
new Claim(ClaimTypes.Name, username)
});

const string sec = "401b09eab3c013d4ca54922bb802bec8fd5318192b0a75f201d8b3727429090fb337591abd3e44453b954555b7a0812e1081c39b740293f765eae731f5a65ed1";
var now = DateTime.UtcNow;
var securityKey = new Microsoft.IdentityModel.Tokens.SymmetricSecurityKey(System.Text.Encoding.Default.GetBytes(sec));
var signingCredentials = new Microsoft.IdentityModel.Tokens.SigningCredentials(securityKey, Microsoft.IdentityModel.Tokens.SecurityAlgorithms.HmacSha256Signature);


//create the jwt
var token =
(JwtSecurityToken)
tokenHandler.CreateJwtSecurityToken(issuer: "http://localhost:50191", audience: "http://localhost:50191",
subject: claimsIdentity, notBefore: issuedAt, expires: expires, signingCredentials: signingCredentials);
var tokenString = tokenHandler.WriteToken(token);

return tokenString;
}
}

ClientesController.cs

public class ClientesController : ApiController
{
[HttpGet]
public IHttpActionResult Lista()        
{
var l = new ParametrosLogica();
var pp = l.Listar(5);

return this.Ok(pp);
}

[HttpGet]
public IHttpActionResult Buscar(int id)
{
var l = new ClientesLogica();
var pp = l.Buscar(id);

return this.Ok(pp);
}
}


Routing by action name WebApi

Si se quieren utilizar nombres especificos de rutas, en lugar de los estandares REST (get, post, put, delete), cambiar en WebApiConfig.cs:

config.Routes.MapHttpRoute(
          name: "ActionApi",
          routeTemplate: "api/{controller}/{action}/{id}",
          defaults: new { id = RouteParameter.Optional }
);

y comentar el config.Routes existente.


Fuente: https://docs.microsoft.com/en-us/aspnet/web-api/overview/web-api-routing-and-actions/routing-in-aspnet-web-api