You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于Hapi基础认证与Angular2的登录系统开发求助

Hey there! I’ve been right where you are—fumbling through Angular + Hapi auth as a newbie, struggling to find complete, working code examples. Let’s fix that by walking through a full, functional setup step by step.


Hapi Server with hapi-auth-basic

First, let’s lock in your backend. I’ll include CORS setup (critical for Angular) and a basic user validation flow (with a reminder to use password hashing in production!).

  1. Install dependencies:

    npm install @hapi/hapi @hapi/basic bcrypt
    
  2. Create your server.js:

    const Hapi = require('@hapi/hapi');
    const Basic = require('@hapi/basic');
    const bcrypt = require('bcrypt');
    
    // In production, pull this from a database!
    const users = {
      demo: {
        username: 'demo',
        // Hash your passwords first! Run bcrypt.hashSync('demo123', 10) to generate this
        password: '$2b$10$Z8HxQrOvV8e8yL7X9kM0uOe7R5T3Y1U9I7K3J5H1G3F1D1S1A1',
        name: 'Demo User',
        id: 'user-1'
      }
    };
    
    // Basic auth validation function
    const validateUser = async (request, username, password) => {
      const user = users[username];
      if (!user) return { credentials: null, isValid: false };
    
      // Compare submitted password to stored hash
      const passwordMatch = await bcrypt.compare(password, user.password);
      const credentials = { id: user.id, name: user.name };
    
      return { isValid: passwordMatch, credentials };
    };
    
    const initServer = async () => {
      const server = Hapi.server({
        port: 3000,
        host: 'localhost',
        routes: {
          cors: {
            origin: ['http://localhost:4200'], // Allow Angular dev server
            credentials: true
          }
        }
      });
    
      // Register basic auth plugin
      await server.register(Basic);
      server.auth.strategy('simple-auth', 'basic', { validate: validateUser });
      server.auth.default('simple-auth'); // Apply auth to all routes by default
    
      // Public route (for login page access)
      server.route({
        method: 'GET',
        path: '/api/public',
        options: { auth: false },
        handler: (req, h) => ({ message: 'Public endpoint—no auth needed!' })
      });
    
      // Protected route (only accessible after login)
      server.route({
        method: 'GET',
        path: '/api/protected',
        handler: (req, h) => ({
          message: `Welcome back, ${req.auth.credentials.name}! This is protected content.`
        })
      });
    
      await server.start();
      console.log(`Server running at ${server.info.uri}`);
    };
    
    process.on('unhandledRejection', err => {
      console.error(err);
      process.exit(1);
    });
    
    initServer();
    

Angular Frontend Setup

Now let’s build the Angular side: auth service, login component, route guards, and protected pages.

1. Auth Service (auth.service.ts)

This handles sending credentials to the backend, persisting login state, and attaching auth headers to requests.

import { Injectable } from '@angular/core';
import { HttpClient, HttpHeaders } from '@angular/common/http';
import { BehaviorSubject, Observable } from 'rxjs';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private loggedIn$ = new BehaviorSubject<boolean>(false);
  private apiBaseUrl = 'http://localhost:3000/api';

  constructor(private http: HttpClient) {
    // Check localStorage for saved credentials on app load
    if (localStorage.getItem('auth-credentials')) {
      this.loggedIn$.next(true);
    }
  }

  get isLoggedIn(): Observable<boolean> {
    return this.loggedIn$.asObservable();
  }

  // Send login request to backend
  login(username: string, password: string): Observable<any> {
    const encodedCreds = btoa(`${username}:${password}`);
    const headers = new HttpHeaders({
      'Authorization': `Basic ${encodedCreds}`
    });

    // We hit a protected endpoint to validate credentials
    return this.http.get(`${this.apiBaseUrl}/protected`, { headers });
  }

  // Update login state and save/remove credentials
  setLoginStatus(isLoggedIn: boolean, credentials?: string): void {
    this.loggedIn$.next(isLoggedIn);
    if (isLoggedIn && credentials) {
      localStorage.setItem('auth-credentials', credentials);
    } else {
      localStorage.removeItem('auth-credentials');
    }
  }

  logout(): void {
    this.setLoginStatus(false);
  }

  // Get auth headers for protected requests
  getAuthHeaders(): HttpHeaders {
    const creds = localStorage.getItem('auth-credentials');
    if (creds) {
      return new HttpHeaders({ 'Authorization': `Basic ${creds}` });
    }
    return new HttpHeaders();
  }
}

2. Login Component (login.component.ts)

Handles the login form submission and error handling.

import { Component } from '@angular/core';
import { AuthService } from '../auth.service';
import { Router } from '@angular/router';

@Component({
  selector: 'app-login',
  templateUrl: './login.component.html'
})
export class LoginComponent {
  username = '';
  password = '';
  errorMsg = '';

  constructor(private authService: AuthService, private router: Router) {}

  onSubmit(): void {
    this.authService.login(this.username, this.password).subscribe({
      next: () => {
        const encodedCreds = btoa(`${this.username}:${this.password}`);
        this.authService.setLoginStatus(true, encodedCreds);
        this.router.navigate(['/dashboard']);
      },
      error: () => {
        this.errorMsg = 'Invalid username or password';
      }
    });
  }
}

Login component HTML (login.component.html):

<div class="login-form">
  <h2>Log In</h2>
  <form (ngSubmit)="onSubmit()">
    <div>
      <label>Username:</label>
      <input type="text" [(ngModel)]="username" name="username" required>
    </div>
    <div>
      <label>Password:</label>
      <input type="password" [(ngModel)]="password" name="password" required>
    </div>
    <button type="submit">Login</button>
    <p class="error" *ngIf="errorMsg">{{ errorMsg }}</p>
  </form>
</div>

3. Route Guard (auth.guard.ts)

Prevents access to protected pages unless the user is logged in.

import { Injectable } from '@angular/core';
import { CanActivate, Router } from '@angular/router';
import { AuthService } from './auth.service';
import { map } from 'rxjs/operators';

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

  canActivate() {
    return this.authService.isLoggedIn.pipe(
      map(isLoggedIn => {
        if (!isLoggedIn) {
          this.router.navigate(['/login']);
          return false;
        }
        return true;
      })
    );
  }
}

4. Routing (app-routing.module.ts)

Set up public and protected routes.

import { NgModule } from '@angular/core';
import { RouterModule, Routes } from '@angular/router';
import { LoginComponent } from './login/login.component';
import { DashboardComponent } from './dashboard/dashboard.component';
import { AuthGuard } from './auth.guard';

const routes: Routes = [
  { path: '', redirectTo: '/login', pathMatch: 'full' },
  { path: 'login', component: LoginComponent },
  { path: 'dashboard', component: DashboardComponent, canActivate: [AuthGuard] },
  { path: '**', redirectTo: '/login' }
];

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

5. Protected Dashboard Component

Example of a page that fetches protected data from the backend.

import { Component, OnInit } from '@angular/core';
import { AuthService } from '../auth.service';
import { HttpClient } from '@angular/common/http';

@Component({
  selector: 'app-dashboard',
  templateUrl: './dashboard.component.html'
})
export class DashboardComponent implements OnInit {
  protectedData: any;

  constructor(private authService: AuthService, private http: HttpClient) {}

  ngOnInit(): void {
    this.http.get('http://localhost:3000/api/protected', {
      headers: this.authService.getAuthHeaders()
    }).subscribe(data => {
      this.protectedData = data;
    });
  }

  logout(): void {
    this.authService.logout();
    this.router.navigate(['/login']);
  }
}

Key Notes for Production

  • Password Security: Never store plain text passwords! Use bcrypt (as shown in the Hapi code) to hash passwords before saving them.
  • Persistent Login: The example uses localStorage for simplicity, but in production, use HttpOnly cookies for better security.
  • CORS: Make sure to restrict the CORS origin to your production Angular URL (not *).

内容的提问来源于stack exchange,提问作者Marien

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.20 07:09:37