Angular场景下如何实现新开标签页粘贴URL时保留token维持登录状态
你遇到的问题根源是sessionStorage属于标签页级别的会话存储,不同标签页的sessionStorage数据完全隔离无法共享,所以新打开的标签页无法获取原有标签页存储的有效token。以下是两种可直接落地的实现方案:
方案1:替换存储介质为localStorage(实现成本最低)
localStorage是同域下跨标签页持久化存储,只要不手动清除就一直存在,完全满足新标签页共享token的需求,改动量极小。
需要修改的代码
- 修改登录服务的token存储逻辑
将原登录服务中sessionStorage写入逻辑替换为localStorage,对应退出登录逻辑也同步修改为清除localStorage的token:
login(model: any): Observable<any> { let headers = new HttpHeaders({ 'Content-Type': 'application/json' }); return this.http.post(API URL, model, { headers: headers }).pipe( map((res: any) => { var obj = jwt_decode(res.responseObject); // 替换为localStorage存储 localStorage.setItem('auth_token', obj); return true; }), catchError((res: any) => { return Observable.throw(res.error.error); }) ); }
- 修改AuthGuard的token读取逻辑
import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) {} canActivate(){ // 替换为从localStorage读取token if (localStorage.getItem('auth_token')) { return true; } this.router.navigate(['/auth/login']); return false; } }
注意事项
localStorage是持久化存储,用户关闭浏览器再打开仍然会保留token,若需要实现关闭浏览器就失效的效果,可以给token关联过期时间,读取时先校验是否过期,过期则自动清除并跳转到登录页- 做好前端XSS防护,避免不可信第三方脚本读取到存储的token
方案2:保留sessionStorage,通过Broadcast Channel API同步token(安全性更高)
如果不想使用localStorage的持久化特性,需要保留关闭标签页就自动清除token的安全逻辑,可以用HTML5的Broadcast Channel API实现同域下跨标签页通信,同步token数据。
需要修改的代码
- 在AppComponent中添加跨标签页通信逻辑
import { Component, OnInit, OnDestroy } from '@angular/core'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.scss'] }) export class AppComponent implements OnInit, OnDestroy { // 声明同域下共享的通信通道 private authChannel = new BroadcastChannel('auth_token_channel'); ngOnInit() { // 监听其他标签页的通信消息 this.authChannel.onmessage = (e) => { // 收到新标签页的token请求,已有登录态的标签页返回token if (e.data === 'request_auth_token') { const token = sessionStorage.getItem('auth_token'); if (token) { this.authChannel.postMessage({ type: 'auth_token', token: token }); } } // 新标签页收到返回的token后,存入自身的sessionStorage else if (e.data.type === 'auth_token' && !sessionStorage.getItem('auth_token')) { sessionStorage.setItem('auth_token', e.data.token); } }; // 新标签页初始化时主动请求token if (!sessionStorage.getItem('auth_token')) { this.authChannel.postMessage('request_auth_token'); } } ngOnDestroy() { // 销毁时关闭通信通道 this.authChannel.close(); } }
- 修改AuthGuard,增加等待token同步的超时逻辑
import { Injectable } from '@angular/core'; import { CanActivate, Router } from '@angular/router'; import { Observable, of } from 'rxjs'; import { delay, map } from 'rxjs/operators'; @Injectable() export class AuthGuard implements CanActivate { constructor(private router: Router) {} canActivate(): Observable<boolean> | boolean { if (sessionStorage.getItem('auth_token')) { return true; } // 等待300ms获取其他标签页同步的token,超时再跳登录 return of(false).pipe( delay(300), map(() => { if (sessionStorage.getItem('auth_token')) { return true; } this.router.navigate(['/auth/login']); return false; }) ) } }
方案优势
保留了sessionStorage的会话级特性,关闭所有标签页后token自动清除,安全性更高,兼容所有现代浏览器。
内容的提问来源于stack exchange,提问作者Rushiraj Rana
相关产品推荐
相关产品推荐

