如何在Angular中通过Interceptor实现页面刷新后自动登录?
页面刷新后Angular自动登录实现方案
我开发了一个通过Interceptor处理登录的Angular应用,用户登录后从数据库获取JWT令牌和用户数据,将JWT存入localStorage,同时把这些信息保存到session变量中。但页面刷新后会丢失session状态,请问如何通过Interceptor实现页面刷新后的自动登录?需要调整Interceptor文件代码,同时完善auth.service.ts中的autoLogin()函数。
一、完善auth.service.ts
补全登录时的localStorage存储逻辑,同时实现完整的autoLogin功能,在应用初始化时自动恢复session状态:
import { Injectable } from '@angular/core'; import { BehaviorSubject, Observable } from 'rxjs'; import { HttpClient, HttpErrorResponse } from '@angular/common/http'; // 定义Session类型 interface Session { token: string; user: any; } @Injectable({ providedIn: 'root' }) export class AuthService { private session = new BehaviorSubject<Session>({ token: '', user: {} }); loginError: any; private api_url = '你的登录接口地址'; // 替换成实际接口地址 constructor(private _http: HttpClient) { // 应用启动时自动执行自动登录逻辑 this.autoLogin(); } getToken() { const session = this.session.getValue(); return session?.token; } getCurrentUser() { const session = this.session.getValue(); return session?.user; } // 供组件订阅session状态变化 getSessionObservable(): Observable<Session> { return this.session.asObservable(); } login(credentials: any) { this._http.post<Session>(this.api_url, credentials).subscribe( (session: Session) => { // 将token和用户数据存入localStorage localStorage.setItem('token', session.token); localStorage.setItem('user', JSON.stringify(session.user)); // 更新session状态 this.session.next(session); this.loginError = null; }, (error) => { if (error instanceof HttpErrorResponse) { this.loginError = error.error; console.log(this.loginError); } } ); } autoLogin() { const tokenFromLocalStorage = localStorage.getItem('token'); const userFromLocalStorage = localStorage.getItem('user'); // 只有同时存在token和用户数据时才恢复状态 if (tokenFromLocalStorage && userFromLocalStorage) { const parsedUser = JSON.parse(userFromLocalStorage); // 更新BehaviorSubject,恢复session状态 this.session.next({ token: tokenFromLocalStorage, user: parsedUser }); // 可选:如果后端有token验证接口,可在这里调用验证token有效性 // 验证失败则执行登出 // this._http.post('/api/verify-token', { token: tokenFromLocalStorage }) // .subscribe({ // next: () => {}, // error: () => this.logout() // }); } } logout() { // 清空localStorage和session状态 localStorage.removeItem('token'); localStorage.removeItem('user'); this.session.next({ token: '', user: {} }); // 可选:跳转到登录页,需要先注入Router // this.router.navigate(['/login']); } }
二、调整auth-interceptor.service.ts
修改拦截器,排除不需要授权的接口(比如登录接口)避免循环请求,同时优化授权头添加逻辑:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import { Observable, catchError } from 'rxjs'; import { AuthService } from './auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private _auth: AuthService) {} intercept( req: HttpRequest<any>, next: HttpHandler ): Observable<HttpEvent<any>> { // 排除不需要授权的接口,比如登录、token验证接口 const excludedPaths = ['/api/login', '/api/verify-token']; if (excludedPaths.some(path => req.url.includes(path))) { return next.handle(req); } // 生成带授权头的请求 const authorizedReq = this.getAuthorizedRequest(req); return next.handle(authorizedReq).pipe( catchError((err) => { // 遇到401未授权时,执行登出逻辑 if (err instanceof HttpErrorResponse && err.status === 401) { this._auth.logout(); } throw err; }) ); } getAuthorizedRequest(req: HttpRequest<any>) { const token = this._auth.getToken(); // 只有存在有效token时才添加Authorization头 if (token) { return req.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } // 无token则返回原请求 return req; } }
三、注册拦截器
在app.module.ts中注册拦截器,确保它能生效:
import { NgModule } from '@angular/core'; import { BrowserModule } from '@angular/platform-browser'; import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http'; import { AppComponent } from './app.component'; import { AuthInterceptor } from './auth-interceptor.service'; import { AuthService } from './auth.service'; @NgModule({ declarations: [AppComponent], imports: [BrowserModule, HttpClientModule], providers: [ AuthService, { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ], bootstrap: [AppComponent] }) export class AppModule { }
内容的提问来源于stack exchange,提问作者AdamB
相关产品推荐
相关产品推荐

