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

Angular SSR无法保留服务单例状态问题求助

解决Angular SSR刷新时AuthService Token丢失问题

根注入的AuthService在SSR场景下,服务器端每次处理请求都会创建全新的服务实例,内存中存储的Token无法跨请求/页面刷新保留——因为浏览器和服务器属于完全隔离的运行环境,刷新后服务器端无法获取浏览器内存中的Token,导致拦截器无法添加Authorization头,最终请求失败、路由守卫重定向到登录页。

以下是具体解决方案:

1. 持久化存储Token,区分客户端/服务器环境

将Token存储到浏览器的localStorage或Cookie中,同时在AuthService中处理环境差异,避免SSR时访问浏览器API报错:

import { Injectable, PLATFORM_ID, Inject } from '@angular/core';
import { isPlatformBrowser } from '@angular/common';

@Injectable({ providedIn: 'root' })
export class AuthService {
  private _token: string | null = null;

  constructor(@Inject(PLATFORM_ID) private platformId: Object) {
    // 客户端初始化时从localStorage读取Token
    if (isPlatformBrowser(this.platformId)) {
      this._token = localStorage.getItem('authToken');
    }
  }

  get token(): string | null {
    return this._token;
  }

  set token(newToken: string | null) {
    this._token = newToken;
    // 客户端同步Token到localStorage和Cookie(供服务器端读取)
    if (isPlatformBrowser(this.platformId)) {
      if (newToken) {
        localStorage.setItem('authToken', newToken);
        document.cookie = `authToken=${newToken}; path=/; max-age=86400; SameSite=Strict`;
      } else {
        localStorage.removeItem('authToken');
        document.cookie = 'authToken=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT';
      }
    }
  }
}

2. 服务器端从请求中获取Token并注入AuthService

在Angular Universal的server.ts中,通过cookie-parser解析请求Cookie,将Token注入到服务器端的AuthService实例中:

首先安装依赖:

npm install cookie-parser

修改server.ts:

import { ngExpressEngine } from '@nguniversal/express-engine';
import * as cookieParser from 'cookie-parser';
import { AuthService } from './src/app/auth.service';

// 注册Cookie解析中间件
app.use(cookieParser());

app.engine('html', ngExpressEngine({
  bootstrap: AppServerModule,
}));

app.get('*', (req, res) => {
  const authToken = req.cookies.authToken;
  res.render('index', {
    req,
    providers: [
      {
        provide: AuthService,
        useFactory: () => {
          const service = new AuthService();
          service.token = authToken;
          return service;
        },
      },
    ],
  });
});

3. 优化拦截器逻辑

确保只有Token存在时才添加Authorization头,避免空值导致的请求错误:

export const authorizationInterceptor = (req, next) => {
  const authService: AuthService = inject(AuthService);
  let modifiedReq = req;

  if (authService.token) {
    modifiedReq = req.clone({
      setHeaders: {
        Authorization: `Bearer ${authService.token}`,
      },
    });
  }

  return next(modifiedReq);
}

4. 安全注意事项

  • 如果担心XSS攻击,可将Cookie设置为HttpOnly(但客户端JS无法读取,需调整Token获取逻辑,仅通过服务器端传递):
    document.cookie = `authToken=${newToken}; path=/; max-age=86400; HttpOnly; Secure; SameSite=Strict`;
    
  • 确保Token本身是短有效期的,并配合刷新Token机制,降低泄露风险。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.26 12:33:33