Angular 4中JWT令牌过期时如何自动跳转至注销页面
嘿,这个需求在JWT认证的场景里太常见了,结合你的Angular 4 + Node.js Express技术栈,我给你整理几个实用的前端实现方案,保证能搞定自动登出的需求:
JWT令牌本身包含了exp(过期时间戳)字段,我们可以在前端解析这个字段,计算出剩余有效期,然后用定时器自动触发登出逻辑。
步骤大概是这样:
- 用户登录成功拿到令牌后,解析出
exp字段(注意是Unix时间戳,单位是秒,要转成毫秒) - 计算当前时间到过期时间的差值,设置一个
setTimeout,时间到了就执行登出操作 - 如果需要的话,还可以在用户活跃操作时重置定时器(比如点击页面、输入内容),不过你的令牌是1小时固定有效期,这个可以根据需求选择
给你一段Angular 4里的示例代码,放在你的认证服务(比如AuthService)里:
import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; @Injectable() export class AuthService { private tokenExpirationTimer: any; constructor(private router: Router) {} // 登录成功后调用,传入获取到的JWT令牌 setupTokenExpiration(token: string) { // 解析JWT的payload部分 const payload = JSON.parse(atob(token.split('.')[1])); const expirationDate = new Date(payload.exp * 1000); const now = new Date(); const timeUntilExpiration = expirationDate.getTime() - now.getTime(); // 设置定时器,到期自动触发登出 this.tokenExpirationTimer = setTimeout(() => { this.logout(); }, timeUntilExpiration); } logout() { // 清理本地存储的令牌 localStorage.removeItem('jwtToken'); // 跳转到登录页 this.router.navigate(['/login']); // 清除用户状态(比如全局的用户信息变量) // ...其他清理逻辑 // 销毁定时器,避免内存泄漏 if (this.tokenExpirationTimer) { clearTimeout(this.tokenExpirationTimer); } } }
⚠️ 小提醒:这个方案可能受客户端和服务器时间不一致的影响,如果客户端时间比服务器快,会提前登出;慢的话会延后。要解决这个问题,可以让后端在登录接口返回当前服务器时间,前端用这个时间来计算剩余有效期,而不是客户端本地时间。
你的Express后端会校验每个请求的令牌有效性,当令牌过期时,通常会返回401 Unauthorized状态码对吧?那我们可以用Angular的HttpInterceptor(Angular 4.3+开始支持,建议升级到HttpClient模块)来拦截所有请求的响应,一旦捕获到401,就触发登出逻辑。
示例拦截器代码:
import { Injectable } from '@angular/core'; import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor, HttpErrorResponse } from '@angular/common/http'; import { Observable, throwError } from 'rxjs'; import { catchError } from 'rxjs/operators'; import { AuthService } from './auth.service'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private authService: AuthService) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { // 给请求带上令牌(如果本地有存储的话) const token = localStorage.getItem('jwtToken'); if (token) { request = request.clone({ setHeaders: { Authorization: `Bearer ${token}` } }); } return next.handle(request).pipe( catchError((error: HttpErrorResponse) => { // 捕获401状态码,触发登出 if (error.status === 401) { this.authService.logout(); } return throwError(() => error); }) ); } }
别忘了在AppModule里注册这个拦截器:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ ... providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
这个方案的优势是完全依赖后端的判定,不会有时间同步问题;缺点是只有用户发起请求时才会触发,如果用户打开页面后一直不操作,令牌过期了也不会自动登出,直到下次请求才会处理。
把上面两个方案结合起来,既主动监控令牌过期时间,又拦截后端的401响应,这样就能覆盖所有场景:
- 用户操作时,如果令牌过期,后端返回401,拦截器触发登出
- 用户长时间不操作,令牌到期,前端定时器自动触发登出
这样不管哪种情况,都能保证用户在令牌过期后被及时登出,体验更完善。
内容的提问来源于stack exchange,提问作者pavithra prabodha

