You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.27 04:28:18