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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.10 09:10:37