Angular 中的用户登录和登出实现

60 阅读3分钟

在构建 Web 应用时,用户认证是一个不可或缺的功能。Angular 框架提供了一套强大的工具和 API,使得开发者能够轻松实现用户登录和登出功能。本文将叙述如何在 Angular 应用中实现这些功能,包括使用服务、拦截器、路由守卫以及响应式编程技术

认证服务(AuthService)

认证服务是 Angular 认证机制的核心。它通常包含登录(signin)、注册(signup)、登出(signout)等方法。这些方法会与后端 API 进行通信,处理用户的认证状态。

AuthService 的职责

  • 发送 HTTP 请求到后端 API 进行用户认证。
  • 使用 BehaviorSubject 或其他 Observable 保存用户的登录状态。
  • 提供一个方法供组件订阅,以便响应登录状态的变化。

示例代码

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";

@Injectable({
  providedIn: "root",
})
export class AuthService {
  private signedIn$: BehaviorSubject<boolean> = new BehaviorSubject<boolean>(false);

  // 提供外部访问的 Observable
  get signedInObservable(): Observable<boolean> {
    return this.signedIn$.asObservable();
  }

  constructor(private http: HttpClient) {}

  signup(credentials: {
    username: string;
    password: string;
    passwordConfirmation: string;
  }) {
    // 发送注册请求到后端
    return this.http.post("/auth/signup", credentials);
  }

  signin(credentials: { username: string; password: string }) {
    this.http.post("/auth/signin", credentials).subscribe(
      () => {
        // 假设后端在登录成功后返回200 OK
        this.signedIn$.next(true);
      },
      error => {
        // 处理错误情况
        console.error('Signin error', error);
      }
    );
  }

  signout() {
    this.http.post("/auth/signout", {}).subscribe(
      () => {
        this.signedIn$.next(false);
      },
      error => {
        // 处理错误情况
        console.error('Signout error', error);
      }
    );
  }

  // 新增 isAuthenticated 方法
  isAuthenticated(): boolean {
    return this.signedIn$.value;
  }
}

网络请求拦截器(AddCredentials)

为了确保发送到服务器的每个 HTTP 请求都包含必要的凭证(如 cookies),我们需要创建一个 HTTP 拦截器。

AddCredentials 拦截器的职责

  • 克隆每个 HTTP 请求并设置 withCredentialstrue
  • 添加必要的认证头部信息,如 Authorization

示例代码

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

@Injectable()
export class AddCredentialsInterceptor implements HttpInterceptor {
  intercept(req: HttpRequest<any>, next: HttpHandler) {
    const modifiedReq = req.clone({
      withCredentials: true,
    });
    return next.handle(modifiedReq);
  }
}

组件中的登录和登出

组件使用 AuthService 来处理用户的登录和登出操作。通过订阅 AuthService 中的状态 Observable,组件可以响应状态变化并更新 UI。

登出组件示例

import { Component, OnInit, OnDestroy } from "@angular/core";
import { AuthService, Router } from "@angular/router";
import { Subscription } from "rxjs";

@Component({
  selector: "app-signout",
  templateUrl: "./signout.component.html",
  styleUrls: ["./signout.component.css"],
})
export class SignoutComponent implements OnInit, OnDestroy {
  private signedInSubscription: Subscription;

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

  ngOnInit() {
    // 订阅登录状态变化
    this.signedInSubscription = this.authService.signedInObservable.subscribe(
      (signedIn) => {
        if (!signedIn) {
          this.router.navigateByUrl("/");
        }
      }
    );

    this.authService.signout();
  }

  ngOnDestroy() {
    // 组件销毁时取消订阅
    if (this.signedInSubscription) {
      this.signedInSubscription.unsubscribe();
    }
  }
}

路由守卫

路由守卫用于保护某些路由,确保只有认证的用户才能访问。在每次路由跳转前,守卫会检查用户的登录状态。

路由守卫示例

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

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

  canActivate(
    route: ActivatedRouteSnapshot,
    state: RouterStateSnapshot
  ): Observable<boolean> | Promise<boolean> | boolean {
    // 使用 Observable 来处理异步的认证状态检查
    return this.authService.signedInObservable.pipe(
      map(signedIn => {
        if (!signedIn) {
          // 如果用户未登录,则导航到登出页面
          this.router.navigate(["/signout"]);
          return false;
        }
        return true;
      })
    );
  }
}

总结

在Angular应用中实现用户登录和登出功能,我们依赖于认证服务(AuthService)来处理与后端的通信,并维护用户的登录状态。AuthService使用BehaviorSubject来保存登录状态,并提供isAuthenticated()方法来获取当前状态。组件通过订阅AuthService的signedInObservable来响应登录状态的变化,使用async管道在模板中绑定这些异步数据。

登出功能通过调用AuthService的signout方法实现,该方法发送登出请求到后端,并在成功登出后更新登录状态。组件的构造函数中调用此方法,并根据状态更新导航到相应路由。

此外,我们使用路由守卫(AuthGuard)来保护需要认证的路由。AuthGuard检查用户的登录状态,如果用户未登录,则导航到登出页面。通过异步的canActivate方法,我们可以确保路由守卫在用户状态变化时正确响应。

总的来说,Angular的响应式编程特性使得实现用户认证变得简洁而高效,提高了应用的安全性和用户体验。