iframe内嵌页面:跨域通讯

风晴雪sheep

应用场景

在项目开发中,若遇到在系统中内嵌其他平台页面,可做参考。
(注:开发环境Angular8.1.0ng-zorro-antd:~8.0.2,前端容器nginx:1.10.1。)

实现步骤

  • 在本系统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 *;
    …………
} 
阅读 3.9k
8 声望
5 粉丝
0 条评论
8 声望
5 粉丝
文章目录
宣传栏