在构建 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 请求并设置
withCredentials
为true
。 - 添加必要的认证头部信息,如
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的响应式编程特性使得实现用户认证变得简洁而高效,提高了应用的安全性和用户体验。