iframe解决跳转登录界面问题

前端开发中难免会遇到需要通过iframe嵌入其他界面的需求。这时候如果被嵌入的页面需要登录获取权限,会出现iframe内出现登录页的情况。一般都会要求做到无感知登录。具体如何实现,视情况而定,大致如下:

  • 同源环境下,可提前进行登录请求,在localstorage、cookie、session存储token、userId等信息后,进行iframe访问。
  • 跨域时较为复杂,需要同时修改iframe内外部两个系统,本文主要是解决这种情况

整体思路

1.外部系统调用特定接口获取内部系统的token、userId、userName等信息;
2.通过postMessage()方法向iframe内部系统传输token等信息;
3.内部系统通过window.addEventListener('message', event => {})监听传入的message并处理;
4.当内部系统token过期时,通过postMessage()方法向外部系统传送过期标志;
5.外部系统通过window.addEventListener('message', event => {})监听关闭iframe页面或者进行其他操作(跳转登录页、重新进行1-3步等)

外部系统

// 必须在onload之后进行通信,不然iframe内部系统可能无法获取参数
this.$refs.iframe.onload = () => {
  this.postMessage()
}
window.addEventListener('message', this.listenMessage)

// 发送message
postMessage() {
  // 通过某种方式获取登录信息
  const ccInfo = oneFunc()
  /**
   * otherWindow.postMessage(message, targetOrigin, [transfer]);
   * message: 将要发送到其他window的数据
   * targetOrigin:  指定哪些url能接收消息,“*” 表示无限制,或者是一个URI
   * transfer: 是一串和message同时传递的Transferable对象,这些对象的所有权将被转移给消息的接收方,而发送方将不再保留所有权
   */
  this.$refs.iframe.contentWindow.postMessage({
    type: 'iframe',
    data: {
      token: ccInfo.token,
      userId: ccInfo.userId,
      userName: ccInfo.userName,
      role: ccInfo.role,
      originUrl: location.href, // 这个有用
      redirect: '/dashboard',
    }
  }, '*')
}

// 监听iframe页面传输message
listenMessage(event) {
  console.log(`get event from iframe => from: ${event.origin} message: ${event.data}`)
  if (event.data && event.data.type === 'iframe' && event.data.expiredToken) {
    this.$message({
      message: '登录过期',
      type: 'error',
    })
  }
}

主要做3件事:1.获取iframe内部系统的登录信息(交给后台同学);2.向iframe内部系统传登录信息;3.监听内部系统返回的token过期标志。

注意:由于是iframe页面加载完成之后触发的传参,其实此时iframe内部系统其实已经跳转到登录页了!为了避免这种情况,我是通过iframe的src设置为特定路由,不做token校验,没有界面,只监听message,当接收到后续信息之后再重定向到redirect

内部系统

// 监听信息(特定/iframe路由)
window.addEventListener('message', event => {
  if (event.data && event.data.type === 'iframe' && event.data.data.token) {
    const data = event.data.data
    setToken(data.token)
    Cookies.set('is-iframe', true)
    Cookies.set('origin-url', data.originUrl)
    Cookies.set('Cloud-User-Id', data.userId)
    Cookies.set('Cloud-User-Name', data.userName)
    Cookies.set('Cloud-User-Role', data.role)
    setTimeout(() => {
      this.$router.push({ path: data.redirect })
    }, 500)
  }
})

// 在router跳转到login页之前执行
if (Cookies.get('is-iframe')) {
  top.postMessage({
    type: 'iframe',
    expiredToken: true
  }, Cookies.get('origin-url'))
  Cookies.set('is-iframe', false)
  return
}

做两件事:1.创建特定路由/iframe页,监听message信息,进行同login操作;2.在token过期跳转到login页之前,向外部系统传参。

注意:top.postMessage如果不设置targetOrigin会提示跨域错误。所以需要用到外部系统传入的originUrl参数。

Logo

基于 Vue 的企业级 UI 组件库和中后台系统解决方案,为数万开发者服务。

更多推荐