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

如何阻止Angular路由守卫在无用户操作时自动跳转

解决Angular应用闲置10分钟自动跳转首页问题

问题根源

应用自动跳转的核心原因是Token过期后路由守卫验证失败:当前TokenService的isValid()方法仅验证了token的签发者(iss),没有检查token的过期时间(exp)。当token在10分钟后过期,loggedIn()返回false,AfterLoginService守卫会阻止访问受保护路由,触发路由默认跳转行为(回到首页)。

解决方案

1. 完善Token的过期时间验证

修改token.service.ts,在isValid()方法中加入token过期时间检查:

import { Injectable } from '@angular/core';

@Injectable({
  providedIn: 'root'
})
export class TokenService {

  private iss = {
    login: 'https://Backend/api/login.php',
    signup: 'https://Backend/api/signup.php',
  }

  constructor() { }

  handle(token) {
    this.set(token);
  }

  set(token) {
    localStorage.setItem('token', token);
  }

  get() {
    return localStorage.getItem('token');
  }

  remove() {
    localStorage.removeItem('token');
  }

  isValid() {
    const token = this.get();
    if (!token || token === 'undefined') {
      return false;
    }
    const payload = this.payload(token);
    if (!payload) {
      return false;
    }
    // 新增:检查token是否过期
    const currentTime = Math.floor(Date.now() / 1000);
    if (payload.exp <= currentTime) {
      this.remove(); // 过期后清除无效token
      return false;
    }
    // 验证签发者
    return Object.values(this.iss).includes(payload.iss);
  }

  payload(token) {
    try {
      const payload = token.split('.')[1];
      return this.decode(payload);
    } catch (e) {
      return null; // 处理无效token的异常
    }
  }

  decode(payload) {
    return JSON.parse(atob(payload));
  }

  loggedIn() {
    return this.isValid();
  }
}

2. 优化路由守卫的跳转逻辑

当前守卫仅返回布尔值,Angular会默认跳转至路由配置的初始页。修改AfterLoginService,在验证失败时主动跳转登录页,并保存当前路由地址,方便用户登录后回到原页面:

import { Injectable } from "@angular/core";
import { ActivatedRouteSnapshot, CanActivate, Router, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs';
import { TokenService } from './token.service';

@Injectable()
export class AfterLoginService implements CanActivate {

  constructor(private Token: TokenService, private router: Router) { }

  canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean | Observable<boolean> | Promise<boolean> {
    if (this.Token.loggedIn()) {
      return true;
    }
    // 保存当前路由,登录后跳转回来
    localStorage.setItem('redirectUrl', state.url);
    // 主动跳转登录页
    this.router.navigate(['/login']);
    return false;
  }
}

3. 可选:添加用户活动监听(延长会话)

如果希望避免用户闲置时token过期,可以监听用户的鼠标、键盘等操作,重置过期计时器,或者调用后端接口刷新token:

// 新建IdleService
import { Injectable, OnDestroy } from '@angular/core';
import { TokenService } from './token.service';
import { Router } from '@angular/router';
import { Subject } from 'rxjs';
import { takeUntil } from 'rxjs/operators';

@Injectable({ providedIn: 'root' })
export class IdleService implements OnDestroy {
  private destroy$ = new Subject<void>();
  private idleTimer: any;
  private readonly IDLE_TIMEOUT = 10 * 60 * 1000; // 10分钟

  constructor(private tokenService: TokenService, private router: Router) {
    this.initIdleListener();
  }

  private initIdleListener() {
    // 监听用户活动
    ['mousemove', 'keydown', 'click', 'scroll'].forEach(event => {
      window.addEventListener(event, () => this.resetIdleTimer());
    });
    this.resetIdleTimer();
  }

  private resetIdleTimer() {
    clearTimeout(this.idleTimer);
    this.idleTimer = setTimeout(() => {
      // 闲置超时后的处理:可以提醒用户,或自动刷新token
      if (this.tokenService.loggedIn()) {
        // 示例:调用后端刷新token,若刷新失败再跳转登录
        // this.authService.refreshToken().pipe(takeUntil(this.destroy$)).subscribe({
        //   next: () => this.resetIdleTimer(),
        //   error: () => {
        //     this.tokenService.remove();
        //     this.router.navigate(['/login']);
        //   }
        // });
      }
    }, this.IDLE_TIMEOUT);
  }

  ngOnDestroy() {
    this.destroy$.next();
    this.destroy$.complete();
    clearTimeout(this.idleTimer);
  }
}

在AppComponent中注入该服务以启用监听:

import { Component } from '@angular/core';
import { IdleService } from './idle.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
})
export class AppComponent {
  constructor(private idleService: IdleService) {}
}

总结

  • 核心是完善token的过期验证,确保守卫能准确判断用户登录状态
  • 优化守卫的跳转逻辑,提升用户体验
  • 可选的闲置监听可以避免用户无操作时被强制退出

内容的提问来源于stack exchange,提问作者SkullSrusher

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 00:40:23