Angular v12中OIDC静默刷新触发变更检测后图标/图片消失求助
Angular 12 + OIDC静默刷新后图标/图片消失问题
问题详情
基于Angular v12开发的应用,包含SVG、PNG格式图片及@ng-bootstrap/ng-bootstrap库导入的图标,页面初始加载时所有资源渲染正常。配置OIDC认证后,当调用silentRenewal方法触发内部变更检测完成,页面中大量图标和图片消失:
- 图标/图片的URL验证正确,可在新标签页正常打开,但无法在当前页面渲染
- 问题影响头部、侧边栏、仪表盘等多个组件的图标显示
- 仅在Chrome、Safari、Edge浏览器中复现,Firefox浏览器下显示正常
环境依赖(package.json)
{ "name": "xyz", "version": "0.0.0", "scripts": { "ng": "ng", "start": "ng serve", "prebuild": "npm --no-git-tag-version version patch", "build": "ng build", "watch": "ng build --watch --configuration development", "test": "ng test", "lint": "ng lint" }, "private": true, "dependencies": { "@angular/animations": "~12.2.0", "@angular/common": "~12.2.0", "@angular/compiler": "~12.2.0", "@angular/core": "~12.2.0", "@angular/forms": "~12.2.0", "@angular/localize": "~12.2.0", "@angular/platform-browser": "~12.2.0", "@angular/platform-browser-dynamic": "~12.2.0", "@angular/router": "~12.2.0", "@circlon/angular-tree-component": "^11.0.4", "@ng-bootstrap/ng-bootstrap": "^10.0.0", "@ngrx/effects": "^13.0.2", "@ngrx/store": "^13.0.2", "@ngrx/store-devtools": "^13.0.2", "@tinymce/tinymce-angular": "^4.2.4", "ag-grid-angular": "^27.0.0", "ag-grid-community": "^27.0.1", "angular-multi-step-form": "^1.2.8", "bootstrap": "^4.5.0", "bs-stepper": "^1.7.0", "chart.js": "^2.9.3", "file-saver": "^2.0.5", "html2canvas": "^1.4.1", "lodash": "^4.17.21", "moment": "^2.29.3", "ng-multiselect-dropdown": "^0.3.8", "ng-template": "^1.0.0", "ng2-charts": "^2.2.3", "ngx-image-cropper": "^6.1.0", "ngx-toastr": "^14.2.2", "oidc-client": "^1.11.5", "rxjs": "~6.6.0", "tinymce": "^5.10.1", "tslib": "^2.3.0", "xlsx": "^0.18.5", "zone.js": "~0.11.4" }, "devDependencies": { "@angular-devkit/build-angular": "~12.2.11", "@angular-eslint/builder": "12.7.0", "@angular-eslint/eslint-plugin": "12.7.0", "@angular-eslint/eslint-plugin-template": "12.7.0", "@angular-eslint/schematics": "12.7.0", "@angular-eslint/template-parser": "12.7.0", "@angular/cli": "~12.2.11", "@angular/compiler-cli": "~12.2.0", "@types/crypto-js": "^4.1.1", "@types/file-saver": "^2.0.5", "@types/jasmine": "~3.8.0", "@types/lodash": "^4.14.181", "@types/node": "^12.11.1", "@typescript-eslint/eslint-plugin": "4.28.2", "@typescript-eslint/parser": "4.28.2", "eslint": "^7.26.0", "jasmine-core": "~3.8.0", "karma": "~6.3.0", "karma-chrome-launcher": "~3.1.0", "karma-coverage": "~2.0.3", "karma-jasmine": "~4.0.0", "karma-jasmine-html-reporter": "~1.7.0", "typescript": "~4.3.5" } }
已尝试无效的解决方案
- 组件级别设置
ChangeDetectionStrategy.OnPush,并在afterViewInit生命周期中使用ChangeDetectorRef手动触发变更检测 - 将图片和图标URL改为绝对路径
可能的解决方案
1. 静默刷新完成后强制重新加载媒体元素
OIDC静默刷新会创建隐藏iframe,Chromium内核浏览器可能因此干扰页面内img/svg元素的渲染上下文。在静默刷新成功后,手动重置媒体元素的src/href属性:
import { Injectable } from '@angular/core'; import { UserManager } from 'oidc-client'; @Injectable({ providedIn: 'root' }) export class AuthService { private userManager: UserManager; constructor() { const oidcConfig = { // 你的OIDC配置项 silent_redirect_uri: '/silent-renew.html', // ...其他配置 }; this.userManager = new UserManager(oidcConfig); // 监听静默刷新成功事件 this.userManager.events.addSilentRenewSuccess(() => { this.reloadAllMediaElements(); }); } private reloadAllMediaElements(): void { // 处理img元素 document.querySelectorAll('img').forEach(img => { const tempSrc = img.src; img.src = ''; img.src = tempSrc; }); // 处理SVG use元素 document.querySelectorAll('svg use').forEach(use => { const href = use.getAttribute('href') || use.getAttribute('xlink:href'); if (href) { use.removeAttribute('href'); use.removeAttribute('xlink:href'); setTimeout(() => { href.startsWith('#') ? use.setAttribute('href', href) : use.setAttribute('xlink:href', href); }, 50); } }); } }
2. 临时禁用全局变更检测
Angular变更检测可能在OIDC操作后误判媒体元素状态,导致不渲染。在静默刷新前后临时禁用并恢复变更检测:
import { Injectable, ApplicationRef } from '@angular/core'; import { UserManager } from 'oidc-client'; @Injectable({ providedIn: 'root' }) export class AuthService { constructor(private appRef: ApplicationRef) {} async performSilentRenew(): Promise<void> { // 保存原始detach方法,临时禁用变更检测 const originalDetach = this.appRef.detach; this.appRef.detach = () => {}; try { await this.userManager.signinSilent(); } finally { // 恢复变更检测并手动触发一次 this.appRef.detach = originalDetach; this.appRef.tick(); // 强制刷新媒体元素 this.reloadAllMediaElements(); } } // 复用上面的reloadAllMediaElements方法 private reloadAllMediaElements(): void { // ...同上 } }
3. 升级OIDC客户端库
当前使用的oidc-client@1.11.5版本较旧,存在Chromium浏览器下的已知兼容性问题。建议升级到官方维护的oidc-ts-client(原库的TypeScript重写版):
npm uninstall oidc-client npm install oidc-ts-client@latest
修改代码中的导入路径:
// 替换原导入 import { UserManager } from 'oidc-ts-client';
4. 使用DomSanitizer安全管道
变更检测后Angular的DomSanitizer可能重新标记资源URL为不安全,导致无法渲染。创建安全URL管道并在模板中使用:
步骤1:创建安全管道
import { Pipe, PipeTransform } from '@angular/core'; import { DomSanitizer, SafeResourceUrl } from '@angular/platform-browser'; @Pipe({ name: 'safeResourceUrl' }) export class SafeResourceUrlPipe implements PipeTransform { constructor(private sanitizer: DomSanitizer) {} transform(url: string): SafeResourceUrl { return this.sanitizer.bypassSecurityTrustResourceUrl(url); } }
步骤2:在模板中应用
<!-- 图片 --> <img [src]="imagePath | safeResourceUrl" alt="图片描述"> <!-- SVG图标 --> <svg><use [href]="iconPath | safeResourceUrl"></use></svg>
内容的提问来源于stack exchange,提问作者Naveen Gupta
相关产品推荐
相关产品推荐

