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
相关产品推荐
相关产品推荐

