Publicada:

Angular 14 - Tutorial de autenticación básica con ejemplo

Tutorial creado con Angular 14.2.12

Otras versiones disponibles:

Este es un ejemplo de cómo configurar una página de inicio de sesión simple usando Angular 14 y autenticación HTTP básica.

Ejemplo de aplicación Angular 14

La aplicación de ejemplo es bastante mínima y contiene solo 2 páginas para demostrar la autenticación básica en Angular 14:

  • /login - página de inicio de sesión pública con campos de nombre de usuario y contraseña; al enviar, la página envía una solicitud POST a la API para autenticar las credenciales del usuario; si tiene éxito, la aplicación Angular crea datos de autenticación básicos a partir de la nombre de usuario y contraseña (con codificación base64) para realizar solicitudes autenticadas para proteger las rutas API.
  • / - página de inicio segura que muestra una lista de usuarios obtenida de un punto final API seguro utilizando los datos básicos de autenticación creados después de un inicio de sesión exitoso.

Formulario de inicio de sesión con formularios reactivos angulares

El formulario de inicio de sesión en el ejemplo está construido con la biblioteca Reactive Forms que viene como parte del marco Angular (en @angular/forms). Utiliza un enfoque basado en modelos para crear, validar y manejar formularios en Angular. Para un tutorial de formas reactivas más detallado ver Angular 14 - Ejemplo de Validación de Formularios Reactivos (Reactive Forms).

Estado compartido con RxJS

Los Sujetos y Observables RxJS se utilizan para administrar el estado compartido en la aplicación Angular. Por ejemplo, el servicio de autenticación con un RxJS BehaviorSubject administra el usuario que ha iniciado sesión actualmente y lo expone al resto de la aplicación con un RxJS Observable al que cualquier componente Angular puede suscribirse y recibir una notificación cuando el usuario inicia o cierra sesión.

API de servidor falso

La aplicación Angular se ejecuta con un backend falso de forma predeterminada para permitir que se ejecute completamente en el navegador sin una API de backend real (backend-less), para cambiar a una API real, simplemente elimine o comente la línea debajo del comentario // provider used to create fake backend ubicado en el módulo de la aplicación (/src/app/app.module.ts).

Puede crear su propia API o conectarla con una .NET API o Node .js API disponible a continuación.

Diseñado con Bootstrap 5

La aplicación de inicio de sesión de ejemplo está diseñada con CSS de Bootstrap 5.2; para obtener más información sobre Bootstrap, consulte https://getbootstrap.com/docs/5.2/getting-started/introduction/.

Código en GitHub

El código de la aplicación del tutorial está disponible en GitHub en https://github.com/cornflourblue/angular-14-basic-authentication-example.

Aquí está en acción:: (Ver en StackBlitz en https://stackblitz.com/edit/angular-14-basic-authentication-example)


Ejecute el ejemplo de autenticación básica de Angular 14 localmente

  1. Instalar Node y NPM desde https://nodejs.org.
  2. Descargue o clone el código fuente del proyecto del tutorial desde https://github.com/cornflourblue/angular-14-basic-authentication-example
  3. Instale todos los paquetes npm necesarios ejecutando npm install o npm i desde la línea de comando en la carpeta raíz del proyecto (donde se encuentra el package.json).
  4. Inicie la aplicación ejecutando npm start desde la línea de comando en la carpeta raíz del proyecto, esto compilará la aplicación y la iniciará automáticamente en el navegador en la URL http://localhost:4200.

NOTA: También puede iniciar la aplicación con el comando Angular CLI ng serve --open. Para hacer esto, primero instale Angular CLI globalmente en su sistema con el comando npm install -g @angular/cli.

Para obtener más información sobre cómo configurar su entorno de desarrollo Angular local, consulte Angular - Setup Development Environment.


Conecte la aplicación Angular con una API .NET 6.0

Para obtener detalles completos sobre la API de .NET 6 de ejemplo, consulte la publicación .NET 6.0 - Basic Authentication Tutorial with Example API. Pero para ponerse en marcha rápidamente, solo siga los pasos a continuación.

  1. Instale el SDK de .NET desde https://dotnet.microsoft.com/download.
  2. Descargue o clone el código fuente del proyecto desde https://github.com/cornflourblue/dotnet-6-basic-authentication-api
  3. Inicie la API ejecutando dotnet run desde la línea de comando en la carpeta raíz del proyecto (donde se encuentra el archivo WebApi.csproj), debería ver el mensaje Ahora escuchando: http://localhost:4000.
  4. De vuelta en la aplicación Angular, elimine o comente la línea debajo del comentario // provider used to create fake backend ubicado en /src/app/app.module.ts, luego inicie la aplicación Angular y ahora debería estar conectado con la API de .NET.


Conecte la aplicación Angular con una API de Node.js

Para obtener detalles completos sobre la API de Node.js de ejemplo, consulte la publicación NodeJS - Basic Authentication Tutorial with Example API. Pero para ponerse en marcha rápidamente, solo siga los pasos a continuación.

  1. Descargue o clone el código fuente del proyecto desde https://github.com/cornflourblue/node-basic-authentication-api
  2. Inicie la API ejecutando npm start desde la línea de comando en la carpeta raíz del proyecto, debería ver el mensaje Server listening on port 4000.
  3. De vuelta en la aplicación Angular, elimine o comente la línea debajo del comentario // provider used to create fake backend ubicado en /src/app/app.module.ts, luego inicie la aplicación Angular y ahora debería estar conectada con la API de autenticación basada en funciones de Node.js.


Estructura del proyecto Angular 14

La CLI de Angular se usó para generar la estructura del proyecto base con el comando ng new <nombre del proyecto>, la CLI también se usa para construir y servir la aplicación. Para obtener más información sobre la CLI de Angular, consulte https://angular.io/cli.

La aplicación y la estructura del código del tutorial siguen principalmente las recomendaciones de mejores prácticas de la guía de estilo angular oficial, con algunos de mis propios ajustes aquí y allá.

Estructura de carpetas

Cada función tiene su propia carpeta (home y login), otros códigos compartidos/comunes, como services, models, helpers, etc., se colocan en carpetas con el prefijo _ para diferenciarlos fácilmente y agruparlos en la parte superior de la estructura de carpetas.

Archivos de barril

Los archivos index.ts en cada carpeta son archivos de barril que agrupan los módulos exportados de una carpeta para que puedan importarse usando la ruta de la carpeta en lugar de la ruta completa del módulo y para habilitar la importación de múltiples módulos en una sola importación (por ejemplo, import { AuthenticationService, UserService } from '../_services').

Alias de ruta de TypeScript

Los alias de ruta @app y @environments se han configurado en tsconfig.json que se asignan a la directorios /src/app y /src/environments. Esto permite que las importaciones sean relativas a las carpetas de aplicaciones y entornos al anteponer rutas de importación con alias en lugar de tener que usar rutas relativas largas (por ejemplo, import MyComponent from '../../../MyComponent').

Aquí están los principales archivos del proyecto que contienen la lógica de la aplicación, omití algunos archivos que fueron generados por el comando Angular CLI ng new que yo no cambié.

 

Guardia de autenticación

Ruta: /src/app/_helpers/auth.guard.ts

La protección de autenticación es una protección de ruta angular que se utiliza para evitar que los usuarios no autenticados accedan a rutas restringidas. Lo hace mediante la implementación de la interfaz CanActivate que permite que la protección decida si una ruta se puede activar con el método canActivate(). Si el método devuelve true, la ruta está activada (permitida continuar); de lo contrario, si el método devuelve false, la ruta está bloqueada.

La protección de autenticación utiliza el servicio de autenticación para comprobar si el usuario ha iniciado sesión; si lo ha hecho, devuelve true del método canActivate(), de lo contrario devuelve false y redirige al usuario a la página de inicio de sesión.

Los protectores de ruta angular se adjuntan a las rutas en la configuración del enrutador, este protector de autenticación se usa en app-routing.module.ts para proteger el ruta de la página de inicio.

import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';

import { AuthenticationService } from '@app/_services';

@Injectable({ providedIn: 'root' })
export class AuthGuard implements CanActivate {
    constructor(
        private router: Router,
        private authenticationService: AuthenticationService
    ) { }

    canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
        const user = this.authenticationService.userValue;
        if (user) {
            // logged in so return true
            return true;
        }

        // not logged in so redirect to login page with the return url
        this.router.navigate(['/login'], { queryParams: { returnUrl: state.url } });
        return false;
    }
}
 

Interceptor de autenticación básica

Ruta: /src/app/_helpers/basic-auth.interceptor.ts

El Interceptor de autenticación básica intercepta solicitudes http de la aplicación para agregar credenciales de autenticación básica al encabezado de Autorización si el usuario inició sesión y la solicitud es a la URL de API de la aplicación (environment.apiUrl).

Para interceptar y modificar solicitudes HTTP enviadas desde la aplicación Angular, la clase BasicAuthInterceptor implementa la interfaz HttpInterceptor y el método intercept().

El método intercept() modifica la request antes de pasarla al siguiente controlador HTTP en la tubería de solicitud (next.handle(request)). Los interceptores HTTP se agregan a la canalización de solicitudes en la sección providers del archivo app.module.ts.

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

import { environment } from '@environments/environment';
import { AuthenticationService } from '@app/_services';

@Injectable()
export class BasicAuthInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        // add header with basic auth credentials if user is logged in and request is to the api url
        const user = this.authenticationService.userValue;
        const isLoggedIn = user?.authdata;
        const isApiUrl = request.url.startsWith(environment.apiUrl);
        if (isLoggedIn && isApiUrl) {
            request = request.clone({
                setHeaders: { 
                    Authorization: `Basic ${user.authdata}`
                }
            });
        }

        return next.handle(request);
    }
}
 

Interceptor de errores HTTP

Ruta: /src/app/_helpers/error.interceptor.ts

El interceptor de errores intercepta las respuestas http de la API para verificar si hubo algún error. Si hay una respuesta 401 No autorizado o 403 Prohibido, el usuario se desconecta automáticamente de la aplicación, todos los demás errores se vuelven a enviar al servicio de llamadas para que los maneje.

Para interceptar e inspeccionar las respuestas HTTP devueltas a la aplicación Angular, la clase ErrorInterceptor implementa la interfaz HttpInterceptor y el método intercept().

El método intercept() primero llama al siguiente controlador HTTP en la tubería de solicitud (next.handle(request)), luego canaliza la respuesta a través del operador catchError() de RxJS para detectar cualquier error devuelto. Los interceptores HTTP se agregan a la canalización de solicitudes en la sección providers del archivo app.module.ts.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable, throwError } from 'rxjs';
import { catchError } from 'rxjs/operators';

import { AuthenticationService } from '@app/_services';

@Injectable()
export class ErrorInterceptor implements HttpInterceptor {
    constructor(private authenticationService: AuthenticationService) { }

    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        return next.handle(request).pipe(catchError(err => {
            if ([401, 403].includes(err.status)) {
                // auto logout if 401 response returned from api
                this.authenticationService.logout();
            }

            const error = err.error.message || err.statusText;
            return throwError(() => error);
        }))
    }
}
 

Proveedor de back-end falso

Ruta: /src/app/_helpers/fake-backend.ts

Para ejecutar y probar la aplicación Angular sin una API de back-end real, el ejemplo utiliza un back-end falso que intercepta las solicitudes HTTP de la aplicación Angular y las envía de vuelta "falsas" respuestas Esto lo hace una clase que implementa la interfaz HttpInterceptor de Angular; para obtener más información sobre los interceptores HTTP de Angular, consulte https://angular.io/api/common/http/HttpInterceptor o este artículo.

El backend falso contiene una función handleRoute que verifica si la solicitud coincide con una de las rutas falsas en la declaración de cambio, en este momento esto incluye solicitudes POST a la ruta /users/authenticate para gestionar la autenticación y solicitudes GET a la ruta /users para obtener todos los usuarios.

Las solicitudes a la ruta de autenticación son manejadas por la función authenticate() que verifica el nombre de usuario y la contraseña contra una matriz de users codificados. Si el nombre de usuario y la contraseña son correctos, se devuelve una respuesta ok con los detalles del usuario; de lo contrario, se devuelve una respuesta error.

Las solicitudes a la ruta de obtención de usuarios son manejadas por la función getUsers() que verifica si el usuario ha iniciado sesión llamando a la nueva función auxiliar isLoggedIn(). Si el usuario ha iniciado sesión, se devuelve una respuesta ok() con la matriz completa de users; de lo contrario, se devuelve una respuesta 401 Unauthorized llamando al nueva función auxiliar unauthorized().

Si la solicitud no coincide con ninguna de las rutas falsas, se pasa como una solicitud HTTP real a la API de backend.

import { Injectable } from '@angular/core';
import { HttpRequest, HttpResponse, HttpHandler, HttpEvent, HttpInterceptor, HTTP_INTERCEPTORS } from '@angular/common/http';
import { Observable, of, throwError } from 'rxjs';
import { delay, mergeMap, materialize, dematerialize } from 'rxjs/operators';

import { User } from '@app/_models';

const users: User[] = [{ id: 1, username: 'test', password: 'test', firstName: 'Test', lastName: 'User' }];

@Injectable()
export class FakeBackendInterceptor implements HttpInterceptor {
    intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
        const { url, method, headers, body } = request;

        // wrap in delayed observable to simulate server api call
        return of(null)
            .pipe(mergeMap(handleRoute))
            .pipe(materialize()) // call materialize and dematerialize to ensure delay even if an error is thrown (https://github.com/Reactive-Extensions/RxJS/issues/648)
            .pipe(delay(500))
            .pipe(dematerialize());

        function handleRoute() {
            switch (true) {
                case url.endsWith('/users/authenticate') && method === 'POST':
                    return authenticate();
                case url.endsWith('/users') && method === 'GET':
                    return getUsers();
                default:
                    // pass through any requests not handled above
                    return next.handle(request);
            }    
        }

        // route functions

        function authenticate() {
            const { username, password } = body;
            const user = users.find(x => x.username === username && x.password === password);
            if (!user) return error('Username or password is incorrect');
            return ok({
                id: user.id,
                username: user.username,
                firstName: user.firstName,
                lastName: user.lastName
            })
        }

        function getUsers() {
            if (!isLoggedIn()) return unauthorized();
            return ok(users);
        }

        // helper functions

        function ok(body?: any) {
            return of(new HttpResponse({ status: 200, body }))
        }

        function error(message: string) {
            return throwError(() => ({ error: { message } }));
        }

        function unauthorized() {
            return throwError(() => ({ status: 401, error: { message: 'Unauthorized' } }));
        }

        function isLoggedIn() {
            return headers.get('Authorization') === `Basic ${window.btoa('test:test')}`;
        }
    }
}

export let fakeBackendProvider = {
    // use fake backend in place of Http service for backend-less development
    provide: HTTP_INTERCEPTORS,
    useClass: FakeBackendInterceptor,
    multi: true
};
 

Modelo de usuario

Ruta: /src/app/_models/user.ts

El modelo de usuario es una pequeña interfaz que define las propiedades de un usuario.

export interface User {
    id: number;
    username: string;
    password: string;
    firstName: string;
    lastName: string;
    authdata?: string;
}
 

Servicio de autenticación

Ruta: /src/app/_services/authentication.service.ts

El servicio de autenticación se utiliza para iniciar sesión & cierre de sesión de la aplicación Angular, notifica a otros componentes cuando el usuario inicia sesión & out, y permite el acceso al usuario actualmente conectado.

Cómo utiliza el servicio RxJS

Los sujetos y observables de RxJS se utilizan para almacenar el objeto de usuario actual y notificar a otros componentes cuando el usuario inicia y cierra sesión en la aplicación. Los componentes angulares pueden subscribe() a la propiedad pública user: Observable para recibir notificaciones de los cambios, y las notificaciones se envían cuando el método this.userSubject.next() se llama en los métodos login() y logout(), pasando el argumento a cada suscriptor.

El BehaviorSubject de RxJS es un tipo especial de asunto (Subject) que mantiene el valor actual y lo emite a los nuevos suscriptores tan pronto como se suscriben, mientras que los Sujetos normales no almacenan el valor actual y solo emite valores que se publican después de crear una suscripción. Para obtener más información sobre la comunicación de componentes con RxJS, consulte Angular 14 - Comunicación entre componentes con RxJS Observable y Subject.

Métodos y propiedades del servicio de autenticación

El método login() envía las credenciales del usuario a la API a través de una solicitud HTTP POST para la autenticación. Si tiene éxito, los datos básicos de autenticación del usuario (nombre de usuario y contraseña codificados en base64) se agregan al objeto de usuario y se almacenan en localStorage para mantener la sesión del usuario entre las actualizaciones de la página. Luego, el objeto de usuario se publica para todos los suscriptores con la llamada a this.userSubject.next(user);.

Los datos básicos de autenticación son utilizados por el interceptor de autenticación básica anterior para configurar el encabezado de autorización de las solicitudes http realizadas para proteger los puntos finales de API.

El método logout() elimina el objeto de usuario actual del almacenamiento local, publica null en userSubject para notificar a todos los suscriptores que el usuario ha cerrado la sesión y navega a la página /login.

El constructor() del servicio inicializa el userSubject con el objeto de usuario de localStorage, lo que permite al usuario permanecer conectado entre actualizaciones de página o después de cerrar el navegador. La propiedad pública user se establece en this.userSubject.asObservable();, lo que permite que otros componentes se suscriban al Observable user pero no les permita publicar en el userSubject, esto es así que solo se puede iniciar y cerrar sesión en la aplicación a través del servicio de autenticación.

El captador userValue permite que otros componentes obtengan fácilmente el valor del usuario conectado actualmente sin tener que suscribirse al Observable user.

NOTA: si no le gusta la idea de almacenar los detalles del usuario actual en el almacenamiento local, todo lo que necesita hacer es cambiar las 3 referencias a localStorage en este archivo. Otras opciones son el almacenamiento de sesiones, cookies, o simplemente no almacenar los datos del usuario en el navegador, aunque tenga en cuenta que con esta última opción el usuario se desconectará automáticamente si refresca la página.

import { Injectable } from '@angular/core';
import { Router } from '@angular/router';
import { HttpClient } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';
import { map } from 'rxjs/operators';

import { environment } from '@environments/environment';
import { User } from '@app/_models';

@Injectable({ providedIn: 'root' })
export class AuthenticationService {
    private userSubject: BehaviorSubject<User | null>;
    public user: Observable<User | null>;

    constructor(
        private router: Router,
        private http: HttpClient
    ) {
        this.userSubject = new BehaviorSubject(JSON.parse(localStorage.getItem('user')!));
        this.user = this.userSubject.asObservable();
    }

    public get userValue() {
        return this.userSubject.value;
    }

    login(username: string, password: string) {
        return this.http.post<any>(`${environment.apiUrl}/users/authenticate`, { username, password })
            .pipe(map(user => {
                // store user details and basic auth credentials in local storage to keep user logged in between page refreshes
                user.authdata = window.btoa(username + ':' + password);
                localStorage.setItem('user', JSON.stringify(user));
                this.userSubject.next(user);
                return user;
            }));
    }

    logout() {
        // remove user from local storage to log user out
        localStorage.removeItem('user');
        this.userSubject.next(null);
        this.router.navigate(['/login']);
    }
}
 

Servicio de Usuario

Ruta: /src/app/_services/user.service.ts

El servicio de usuario contiene un método para obtener todos los usuarios de la API, lo incluí para demostrar cómo acceder a un punto final seguro de la API con el encabezado de autorización http establecido después de iniciar sesión en la aplicación, el encabezado de autenticación se configura automáticamente con las credenciales de autenticación básicas por el interceptor de autenticación básica.

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

import { environment } from '@environments/environment';
import { User } from '@app/_models';

@Injectable({ providedIn: 'root' })
export class UserService {
    constructor(private http: HttpClient) { }

    getAll() {
        return this.http.get<User[]>(`${environment.apiUrl}/users`);
    }
}
 

Plantilla de componente de inicio

Ruta: /src/app/home/home.component.html

La plantilla del componente de inicio contiene sintaxis de plantilla html y angular 14 para mostrar un mensaje de bienvenida simple y una lista de usuarios desde un punto final de API seguro.

<div class="card mt-4">
    <h4 class="card-header">You're logged in with Angular 14 & Basic HTTP Authentication!!</h4>
    <div class="card-body">
        <h6>Users from secure api end point</h6>
        <div *ngIf="loading" class="spinner-border spinner-border-sm"></div>
        <ul *ngIf="users">
            <li *ngFor="let user of users">{{user.firstName}} {{user.lastName}}</li>
        </ul>
    </div>
</div>
 

Componente de inicio

Ruta: /src/app/home/home.component.ts

El componente de inicio define un componente angular 14 que obtiene todos los usuarios del servicio de usuario y los pone a disposición de la plantilla a través de una propiedad de matriz users.

import { Component } from '@angular/core';
import { first } from 'rxjs/operators';

import { User } from '@app/_models';
import { UserService } from '@app/_services';

@Component({ templateUrl: 'home.component.html' })
export class HomeComponent {
    loading = false;
    users?: User[];

    constructor(private userService: UserService) { }

    ngOnInit() {
        this.loading = true;
        this.userService.getAll().pipe(first()).subscribe(users => {
            this.loading = false;
            this.users = users;
        });
    }
}
 

Plantilla de componente de inicio de sesión

Ruta: /src/app/login/login.component.html

La plantilla del componente de inicio de sesión contiene un formulario de inicio de sesión con campos de nombre de usuario y contraseña. Muestra mensajes de validación para campos no válidos cuando se hace clic en el botón Enviar. El evento de envío de formulario está vinculado al método onSubmit() del componente de inicio de sesión.

El componente utiliza la validación de formularios reactivos para validar los campos de entrada, para obtener más información sobre la validación de formularios reactivos angulares, consulte Angular 14 - Ejemplo de Validación de Formularios Reactivos (Reactive Forms).

<div class="col-md-6 offset-md-3 mt-5">
    <div class="alert alert-info">
        Username: test<br />
        Password: test
    </div>
    <div class="card">
        <h4 class="card-header">Angular 14 Basic Authentication Example</h4>
        <div class="card-body">
            <form [formGroup]="loginForm" (ngSubmit)="onSubmit()">
                <div class="mb-3">
                    <label class="form-label">Username</label>
                    <input type="text" formControlName="username" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.username.errors }" />
                    <div *ngIf="submitted && f.username.errors" class="invalid-feedback">
                        <div *ngIf="f.username.errors.required">Username is required</div>
                    </div>
                </div>
                <div class="mb-3">
                    <label class="form-label">Password</label>
                    <input type="password" formControlName="password" class="form-control" [ngClass]="{ 'is-invalid': submitted && f.password.errors }" />
                    <div *ngIf="submitted && f.password.errors" class="invalid-feedback">
                        <div *ngIf="f.password.errors.required">Password is required</div>
                    </div>
                </div>
                <button [disabled]="loading" class="btn btn-primary">
                    <span *ngIf="loading" class="spinner-border spinner-border-sm me-1"></span>
                    Login
                </button>
                <div *ngIf="error" class="alert alert-danger mt-3 mb-0">{{error}}</div>
            </form>
        </div>
    </div>
</div>
 

Componente de inicio de sesión

Ruta: /src/app/login/login.component.ts

El componente de inicio de sesión utiliza el servicio de autenticación para iniciar sesión en la aplicación. Si el usuario ya ha iniciado sesión, se le redirigirá automáticamente a la página de inicio.

El objeto loginForm: FormGroup define los controles y validadores del formulario, y se utiliza para acceder a los datos ingresados en el formulario. FormGroup es parte del módulo Angular Reactive Forms y está vinculado a la plantilla de inicio de sesión anterior con la directiva [formGroup]="loginForm".

import { Component, OnInit } from '@angular/core';
import { Router, ActivatedRoute } from '@angular/router';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { first } from 'rxjs/operators';

import { AuthenticationService } from '@app/_services';

@Component({ templateUrl: 'login.component.html' })
export class LoginComponent implements OnInit {
    loginForm!: FormGroup;
    loading = false;
    submitted = false;
    error = '';

    constructor(
        private formBuilder: FormBuilder,
        private route: ActivatedRoute,
        private router: Router,
        private authenticationService: AuthenticationService
    ) {
        // redirect to home if already logged in
        if (this.authenticationService.userValue) {
            this.router.navigate(['/']);
        }
    }

    ngOnInit() {
        this.loginForm = this.formBuilder.group({
            username: ['', Validators.required],
            password: ['', Validators.required]
        });
    }

    // convenience getter for easy access to form fields
    get f() { return this.loginForm.controls; }

    onSubmit() {
        this.submitted = true;

        // stop here if form is invalid
        if (this.loginForm.invalid) {
            return;
        }

        this.error = '';
        this.loading = true;
        this.authenticationService.login(this.f.username.value, this.f.password.value)
            .pipe(first())
            .subscribe({
                next: () => {
                    // get return url from route parameters or default to '/'
                    const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
                    this.router.navigate([returnUrl]);
                },
                error: error => {
                    this.error = error;
                    this.loading = false;
                }
            });
    }
}
 

Módulo de enrutamiento de aplicación

Ruta: /src/app/app-routing.module.ts

El enrutamiento para la aplicación Angular se configura como una matriz de Routes, cada componente se asigna a una ruta para que Angular Router sepa qué componente mostrar en función de la URL en la barra de direcciones del navegador. La ruta de origen se protege pasando AuthGuard a la propiedad canActivate de la ruta.

La matriz routes se pasa al método RouterModule.forRoot() que crea un módulo de enrutamiento con todas las rutas de la aplicación configuradas, y también incluye todos los proveedores y directivas de Angular Router como la directiva <router-outlet></router-outlet>. Para obtener más información sobre el enrutamiento angular y la navegación, consulte https://angular.io/guide/router.

import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';

import { HomeComponent } from './home';
import { LoginComponent } from './login';
import { AuthGuard } from './_helpers';

const routes: Routes = [
    { path: '', component: HomeComponent, canActivate: [AuthGuard] },
    { path: 'login', component: LoginComponent },

    // otherwise redirect to home
    { path: '**', redirectTo: '' }
];

@NgModule({
    imports: [RouterModule.forRoot(routes)],
    exports: [RouterModule]
})
export class AppRoutingModule { }
 

Plantilla de componente de aplicación

Ruta: /src/app/app.component.html

La plantilla del componente de la aplicación es la plantilla del componente raíz de la aplicación, contiene la barra de navegación principal que solo se muestra para usuarios autenticados y un componente router-outlet para mostrar el contenido de cada vista basado en la ruta/camino actual.

<!-- nav -->
<nav class="navbar navbar-expand navbar-dark bg-dark px-3" *ngIf="user">
    <div class="navbar-nav">
        <a class="nav-item nav-link" routerLink="/" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Home</a>
        <button class="btn btn-link nav-item nav-link" (click)="logout()">Logout</button>
    </div>
</nav>

<!-- main app container -->
<div class="container">
    <router-outlet></router-outlet>
</div>
 

Componente de la aplicación

Ruta: /src/app/app.component.ts

El componente de la aplicación es el componente raíz de la aplicación, define la etiqueta raíz de la aplicación como <app-root></app-root> con la propiedad selector del decorador @Component().

Se suscribe al observable user en el servicio de autenticación para que pueda mostrar/ocultar de forma reactiva la barra de navegación principal cuando el usuario inicia/cierra sesión en la aplicación. Por lo general, es una buena práctica cancelar la suscripción cuando el componente se destruye para evitar pérdidas de memoria. Esto no es necesario en el componente raíz de la aplicación porque solo se destruirá cuando se cierre toda la aplicación.

El componente de la aplicación contiene un método logout() que se llama desde el enlace de cierre de sesión en la barra de navegación principal de arriba para cerrar la sesión del usuario y redirigirlo a la página de inicio de sesión.

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

import { AuthenticationService } from './_services';
import { User } from './_models';

@Component({ selector: 'app-root', templateUrl: 'app.component.html' })
export class AppComponent {
    user?: User | null;

    constructor(private authenticationService: AuthenticationService) {
        this.authenticationService.user.subscribe(x => this.user = x);
    }

    logout() {
        this.authenticationService.logout();
    }
}
 

Módulo de aplicación

Ruta: /src/app/app.module.ts

El módulo de la aplicación define el módulo raíz de la aplicación junto con los metadatos sobre el módulo. Para obtener más información, consulte https://angular.io/guide/ngmodules.

Aquí es donde se agrega el proveedor de backend falso a la aplicación; para cambiar a un backend real, simplemente elimine los proveedores que se encuentran debajo del comentario // provider used to create fake backend.

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { ReactiveFormsModule } from '@angular/forms';
import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';

// used to create fake backend
import { fakeBackendProvider } from './_helpers';

import { AppComponent } from './app.component';
import { AppRoutingModule } from './app-routing.module';

import { BasicAuthInterceptor, ErrorInterceptor } from './_helpers';
import { HomeComponent } from './home';
import { LoginComponent } from './login';

@NgModule({
    imports: [
        BrowserModule,
        ReactiveFormsModule,
        HttpClientModule,
        AppRoutingModule
    ],
    declarations: [
        AppComponent,
        HomeComponent,
        LoginComponent
    ],
    providers: [
        { provide: HTTP_INTERCEPTORS, useClass: BasicAuthInterceptor, multi: true },
        { provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true },

        // provider used to create fake backend
        fakeBackendProvider
    ],
    bootstrap: [AppComponent]
})
export class AppModule { }
 

Configuración del entorno de producción

Ruta: /src/environments/environment.prod.ts

La configuración del entorno de producción contiene variables necesarias para ejecutar la aplicación en producción. Esto le permite crear la aplicación con una configuración diferente para cada entorno diferente (por ejemplo, producción y desarrollo) sin actualizar el código de la aplicación.

Cuando compila la aplicación para producción con el comando ng build --configuration production, la salida environment.ts se reemplaza por environment.prod.ts.

export const environment = {
    production: true,
    apiUrl: 'http://localhost:4000'
};
 

Configuración del entorno de desarrollo

Ruta: /src/environments/environment.ts

La configuración del entorno de desarrollo contiene variables necesarias para ejecutar la aplicación en desarrollo.

Se accede a la configuración del entorno importando el objeto del entorno en cualquier servicio o componente de Angular con la línea importar {environment} desde '@environments/environment' y acceder a las propiedades en el objeto environment, consulte el servicio de usuario para ver un ejemplo.

export const environment = {
    production: false,
    apiUrl: 'http://localhost:4000'
};
 

Archivo principal index.html

Ruta: /src/index.html

El archivo index.html principal es la página inicial cargada por el navegador que inicia todo. La CLI de Angular (con Webpack bajo el capó) agrupa todos los archivos javascript compilados y los inyecta en el cuerpo de la página index.html para que el navegador pueda cargar y ejecutar los scripts.

<!DOCTYPE html>
<html>
<head>
    <base href="/" />
    <title>Angular 14 - Basic Authentication Tutorial with Example</title>
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- bootstrap css -->
    <link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
    <app-root></app-root>
</body>
</html>
 

Archivo angular main.js

Ruta: /src/main.ts

El archivo main.js es el punto de entrada utilizado por angular para iniciar y arrancar la aplicación.

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

if (environment.production) {
    enableProdMode();
}

platformBrowserDynamic().bootstrapModule(AppModule)
    .catch(err => console.error(err));
 

Polyfills

Ruta: /src/polyfills.ts

Algunas funciones utilizadas por Angular 14 aún no son compatibles de forma nativa con todos los principales navegadores, los polyfills se utilizan para agregar compatibilidad con funciones donde sea necesario para que su aplicación Angular 14 funcione en todos los principales navegadores.

Este archivo es generado por Angular CLI al crear un nuevo proyecto con el comando ng new, he excluido los comentarios en el archivo por razones de brevedad.

import 'zone.js';  // Included with Angular CLI.
 

npm package.json

Ruta: /package.json

El archivo package.json contiene información de configuración del proyecto, incluidas las dependencias del paquete que se instalan cuando ejecuta npm install y las secuencias de comandos que se ejecutan cuando ejecuta npm start o npm run build etc. La documentación completa está disponible en https://docs.npmjs.com/files/package.json.

{
    "name": "angular-14-example",
    "version": "0.0.0",
    "scripts": {
        "ng": "ng",
        "start": "ng serve --open",
        "build": "ng build",
        "watch": "ng build --watch --configuration development",
        "test": "ng test"
    },
    "private": true,
    "dependencies": {
        "@angular/animations": "^14.2.0",
        "@angular/common": "^14.2.0",
        "@angular/compiler": "^14.2.0",
        "@angular/core": "^14.2.0",
        "@angular/forms": "^14.2.0",
        "@angular/platform-browser": "^14.2.0",
        "@angular/platform-browser-dynamic": "^14.2.0",
        "@angular/router": "^14.2.0",
        "rxjs": "~7.5.0",
        "tslib": "^2.3.0",
        "zone.js": "~0.11.4"
    },
    "devDependencies": {
        "@angular-devkit/build-angular": "^14.2.8",
        "@angular/cli": "~14.2.8",
        "@angular/compiler-cli": "^14.2.0",
        "@types/jasmine": "~4.0.0",
        "jasmine-core": "~4.3.0",
        "karma": "~6.4.0",
        "karma-chrome-launcher": "~3.1.0",
        "karma-coverage": "~2.2.0",
        "karma-jasmine": "~5.1.0",
        "karma-jasmine-html-reporter": "~2.0.0",
        "typescript": "~4.7.2"
    }
}
 

TypeScript tsconfig.json

Ruta: /tsconfig.json

El archivo tsconfig.json contiene la configuración base del compilador de TypeScript para todos los proyectos en el espacio de trabajo de Angular, configura cómo se compilará/transpilará el código de TypeScript en JavaScript que el navegador pueda entender. Para obtener más información, consulte https://angular.io/config/tsconfig.

La mayor parte del archivo no ha cambiado desde que Angular CLI lo generó, solo se agregó la propiedad paths al mapa @app y @environments a los directorios /src/app y /src/environments. Esto permite que las importaciones sean relativas a las carpetas de aplicaciones y entornos al anteponer rutas de importación con alias en lugar de tener que usar rutas relativas largas (por ejemplo, import MyComponent from '../../../MyComponent').

/* To learn more about this file see: https://angular.io/config/tsconfig. */
{
    "compileOnSave": false,
    "compilerOptions": {
        "baseUrl": "./",
        "outDir": "./dist/out-tsc",
        "allowSyntheticDefaultImports": true,
        "forceConsistentCasingInFileNames": true,
        "strict": true,
        "noImplicitOverride": true,
        "noPropertyAccessFromIndexSignature": false,
        "noImplicitReturns": true,
        "noFallthroughCasesInSwitch": true,
        "sourceMap": true,
        "declaration": false,
        "downlevelIteration": true,
        "experimentalDecorators": true,
        "moduleResolution": "node",
        "importHelpers": true,
        "target": "es2020",
        "module": "es2020",
        "lib": [
            "es2020",
            "dom"
        ],
        "paths": {
            "@app/*": ["src/app/*"],
            "@environments/*": ["src/environments/*"]
        }
    },
    "angularCompilerOptions": {
        "enableI18nLegacyMessageIdFormat": false,
        "strictInjectionParameters": true,
        "strictInputAccessModifiers": true,
        "strictTemplates": true
    }
}

 


Suscríbete o Sígueme para actualizaciones

Suscríbete a mi canal de YouTube o sígueme en Twitter, Facebook o GitHub para recibir notificaciones cuando publique contenido nuevo.

Aparte de la codificación...

Actualmente estoy intentando viajar por Australia en motocicleta con mi esposa Tina en un par de Royal Enfield Himalayan. Puedes seguir nuestras aventuras en YouTube, Instagram y Facebook.


¿Necesita Ayuda Angular 14?

Buscar fiverr para encontrar ayuda rápidamente de desarrolladores Angular 14 experimentados.