iframe內嵌頁面——跨域通信

應用場景

在項目開發中,若遇到在系統中內嵌其餘平臺頁面,可作參考。
(注:開發環境Angular8.1.0ng-zorro-antd:~8.0.2,前端容器nginx:1.10.1。)html

實現步驟

  • 在本系統html相應位置引入iframe標籤,src爲內嵌平臺的路徑。
<iframe id="iframe" class="iframe-body" [src]="url" frameborder="0"\></iframe>
  • 在本系統的業務邏輯中,注入DomSanitizer服務,獲取到iframe元素後,經過postMessage向內嵌平臺發送消息,獲取登陸權限。同時經過window.addEventListener監聽內嵌系統是否鏈接成功,從而實現跨域通信。
export class RemoteManagerComponent implements OnInit {  

constructor(private sanitizer: DomSanitizer) {}

ngOnit() {
    this.url = this.sanitizer.bypassSecurityTrustResourceUrl(baseUrl + '/#/login');
    this.postMessage(baseUrl);  // baseUrl是http://:8080,即內嵌網頁的服務器地址
    this.addListener(this.activeRoute);  // activeRoute是內嵌頁面菜單路由
}

// 向遠程發消息-受權  
postMessage(baseUrl) {  
    this.loading = true;  
    let count = 10;  
    this.time = setInterval(() => {  
      let iframe = document.getElementById('iframe');  
      iframe['contentWindow'].postMessage('remote', baseUrl);  
      if (count < 0) {  
          clearInterval(this.time);  
          this.time= null;  
          this.notification.error('鏈接失敗,請重試', null);  
          this.router.navigate(['路由']);  
      }  
      count--;    
  }, 1000);  
}  

// 監聽遠程消息  
addListener(activeRoute) {  
    window.addEventListener('message', (e: any) => {  
      clearInterval(this.time);  
      this.time = null;  
      switch (e.data) {  
        case 'success':  
            console.log('已受權');  
            document.getElementById('iframe')['src'] = activeRoute;  
            this.url = this.sanitizer.bypassSecurityTrustResourceUrl(activeRoute);  
            setTimeout(() => {  
                this.loading = false;  
            }, 2000);  
            return;  
        case 'failed':  
            this.notification.error('鏈接失敗,請重試', null, {nzKey: 'failed'});  
            this.router.navigate(['路由']);  
            return;  
        case 'reboot':  
            this.notification.error('鏈接已斷開,請從新鏈接', null, {nzKey: 'reboot'});  
            this.router.navigate(['路由']);  
            return;  
        default:  
            break;  
    }  
  }, false);  
}
  • 在內嵌平臺中一樣經過window.addEventListener監聽遠程消息,接收到消息後經過window.parent.postMessage向父頁面發送消息,代表已鏈接成功。
// 監聽遠程管理-受權  
addEventListener() {  
    window.addEventListener('message', (e: any) => {  
        if (e['source'] !== window.parent || e.data !== 'remote') {  
            return;  
        }  
        this.storeService.save('remoteIp', e.origin);  
        if (localStorage.getItem('isLogin')) {  // 已受權  
            this.storeService.save('remoteHidden', true);  
            window.parent.postMessage('success', e.origin);  
        } else {  // 未受權  
            this.configService.autoLogin({verifycode: 'soc_fw'}).subscribe((data: any) => {  
                if (data.code === 200) {  
                    this.storeService.save('remoteHidden', true);  
                    localStorage.setItem('isLogin', 'true');  
                    this.configService.whoAmI().then(() => {  
                        window.parent.postMessage('success', e.origin);  
                    });  
                } else {  
                    window.parent.postMessage('failed', e.origin);  
                }  
            }, () => {  
                window.parent.postMessage('failed', e.origin);  
          });  
       }  
    }, false);  
}

總結

服務器默認是不被容許跨域的,若遇到跨協議報錯等問題(如本系統爲http協議,內嵌系統爲https協議,會報403跨域錯誤 No 'Access-Control-Allow-Origin' header is present on the requested resource),具體經過兩種方法解決:前端

  • 將二者更改成同協議,如在內嵌系統的nginx文件中增長8334端口;
  • 給內嵌系統的nginx配置響應的header參數:
location / {  
    // 表示服務器能夠接受全部的請求源(Origin),即接受全部跨域的請求。
    add_header Access-Control-Allow-Origin *;
    …………
}
相關文章
相關標籤/搜索