如何在Angular中处理会话过期?(Spring Boot+UAA场景)
嘿Shilpa,刚好我之前做过类似的Angular+Spring Boot+UAA的项目,给你分享几个实用的实现方案,应该能完美解决你的会话过期提示和跳转需求~
Angular中处理UAA会话过期的解决方案
1. 全局HTTP拦截器捕获授权错误状态码
这是最直接的方式——因为当会话过期时,你的Spring Boot后端(或者UAA本身)通常会返回401 Unauthorized或403 Forbidden状态码。我们可以写一个全局HTTP拦截器,监听所有接口的响应,一旦捕获到这些状态码就触发提示和跳转逻辑。
示例拦截器代码
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 { MatSnackBar } from '@angular/material/snack-bar'; // 这里用Angular Material的提示组件,你也可以换成自己项目用的UI库 import { Router } from '@angular/router'; @Injectable() export class AuthInterceptor implements HttpInterceptor { constructor(private snackBar: MatSnackBar, private router: Router) {} intercept(request: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> { return next.handle(request).pipe( catchError((error: HttpErrorResponse) => { // 匹配会话过期常见的状态码 if (error.status === 401 || error.status === 403) { // 弹出过期提示 this.snackBar.open('会话已过期,请重新登录', '关闭', { duration: 3000, verticalPosition: 'top' }); // 清除本地存储的所有认证信息(token、用户数据等) localStorage.removeItem('access_token'); localStorage.removeItem('refresh_token'); // 跳转到登录页 this.router.navigate(['/login']); } return throwError(() => error); }) ); } }
注册拦截器
记得在AppModule的providers数组中注册这个拦截器,让它全局生效:
import { HTTP_INTERCEPTORS } from '@angular/common/http'; import { AuthInterceptor } from './auth.interceptor'; @NgModule({ // ...其他配置 providers: [ { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true } ] }) export class AppModule { }
2. 主动监听Token过期时间
如果你的UAA返回的JWT Token包含exp(过期时间戳)字段,我们可以在登录成功后主动计算过期时间,用定时器提前提示用户,或者到期自动跳转。这种方式能避免用户在操作中途突然遇到401错误,体验更友好。
示例Token监听服务
import { Injectable } from '@angular/core'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; @Injectable({ providedIn: 'root' }) export class TokenExpiryService { private expiryTimer: any; constructor(private router: Router, private snackBar: MatSnackBar) {} // 登录成功后调用,传入token的剩余过期秒数 setTokenExpiry(remainingSeconds: number) { // 提前30秒给用户弹出即将过期的提示 const warningDelay = remainingSeconds * 1000 - 30000; this.expiryTimer = setTimeout(() => { const snackRef = this.snackBar.open('会话即将过期,请重新登录', '立即登录', { duration: 5000, verticalPosition: 'top' }); // 用户点击"立即登录"直接跳转 snackRef.onAction().subscribe(() => this.handleLogout()); }, warningDelay); // 到期后自动执行登出逻辑 setTimeout(() => this.handleLogout(), remainingSeconds * 1000); } private handleLogout() { // 清除本地认证数据 localStorage.clear(); // 弹出过期提示 this.snackBar.open('会话已过期,请重新登录', '关闭', { duration: 3000 }); // 跳转登录页 this.router.navigate(['/login']); // 清除定时器避免内存泄漏 if (this.expiryTimer) clearTimeout(this.expiryTimer); } // 页面刷新时,重新检查token有效性 checkTokenOnPageRefresh() { const token = localStorage.getItem('access_token'); if (!token) return; // 解析JWT的payload部分,获取exp字段 const payload = JSON.parse(atob(token.split('.')[1])); const currentTime = Math.floor(Date.now() / 1000); // 如果token已经过期,直接登出 if (payload.exp < currentTime) { this.handleLogout(); } else { // 设置剩余时间的定时器 this.setTokenExpiry(payload.exp - currentTime); } } }
使用方式
- 在登录组件获取到token后,调用
tokenExpiryService.setTokenExpiry(payload.exp - currentTime) - 在
AppComponent的ngOnInit钩子中调用tokenExpiryService.checkTokenOnPageRefresh(),处理页面刷新的场景
3. 处理Refresh Token过期场景
如果你的应用用了Refresh Token来静默刷新Access Token,当Refresh Token也过期时,同样需要捕获这个错误并处理。可以在刷新Token的服务中添加错误拦截:
示例Refresh Token服务
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Router } from '@angular/router'; import { MatSnackBar } from '@angular/material/snack-bar'; import { catchError, throwError } from 'rxjs'; @Injectable({ providedIn: 'root' }) export class AuthService { constructor( private http: HttpClient, private router: Router, private snackBar: MatSnackBar ) {} refreshAccessToken() { const refreshToken = localStorage.getItem('refresh_token'); return this.http.post('/uaa/oauth/token', { grant_type: 'refresh_token', refresh_token: refreshToken }).pipe( catchError(error => { // Refresh Token过期或无效时,返回400/401状态码 if (error.status === 400 || error.status === 401) { this.snackBar.open('会话已过期,请重新登录', '关闭', { duration: 3000 }); localStorage.clear(); this.router.navigate(['/login']); } return throwError(() => error); }) ); } }
额外注意事项
- 确保你的UAA和Spring Boot后端在会话过期时,能正确返回
401/403状态码,并且没有跨域问题导致前端无法捕获错误。 - 清除本地存储时,要确保所有和认证相关的数据都被移除(比如用户信息、权限数据等),避免残留数据导致后续请求异常。
- 配合Angular路由守卫使用:在
CanActivate守卫中检查Token有效性,防止用户直接通过URL访问受保护页面。
内容的提问来源于stack exchange,提问作者user8030367
相关产品推荐
相关产品推荐

