前后端分离完全跨域实现单点登录(感觉很low但是真香)

背景
公司内部有ABCDEFG…好多系统,然而这些系统都是需要登录才能够进行访问的,可能每一个系统的账号密码啥的都不一样,这让人就很头疼,所以我们出现了一个叫 权限中心 的系统,权限中心就是用来管理所有的项目以及用户账号等,可以实现一个账号根据其中的权限控制可以登陆哪些系统,所以进一步进化到了另外一个项目或者说是功能 统一登录页 愿景就是我的账号登陆统一登录页可以展示我有权限的项目,并且进入每一个项目都不需要再次登陆,按理说实现 单点登陆 不就是种 cookie 就行吗,但是公司内部的项目又出现 主域 完全不一致,这种方式就有行不通了,那既然不行并且还是前后要端分离的项目,我们只能通过url 传参的方式,比如 https://a.xxx.com?token=y23232ew2s232323 但是这种方式会直接把 token 暴露在浏览器地址栏上不安全,所以我们就稍微的变通了一下,就想办法还是这种方式,但是不把token暴露在地址栏上面,所以就有些了下面的内容
一. 统一登录页 UCenter
1.登陆
统一登录页的登陆就是正常的登陆(会走权限中心的接口),登陆成功之后进行系统,系统里面展示的就是一些改账号有权限看到的各个应用
在这里插入图片描述

<swiper ref="mySwiper" class="mySwiper" :options="swiperOption">
  <swiper-slide v-for="(item, index) in swipers" :key="index" class="swiper">
    <div v-for="app in item" :key="app.id" class="swiper-item" @click="handleClickApp(app)">
      <div class="app-icon" :style="iconStyle(app.type)">
        <span>{{ app.typeName }}</span>
      </div>
      <img :src="app.icon === '' ? type_default : app.icon" alt="">
      <p>{{ app.name }}</p>
    </div>
  </swiper-slide>
  <div slot="pagination" class="swiper-pagination" />
</swiper>

2.关于逻辑部分
当用户登陆成功之后,比如点击A这个应用,这个时候我们会执行 linkApp 这个方法,这个方法的主要流程如下:
我们会判断应用的类型是 开发平台 生成的项目还是 正常开发 的项目,因为开发平台生成的项目我们要做一下特殊处理
最后生成一个url,我们会动态创建一个隐藏的iframe,iframe的src指向的就是我们生成的url当然这个url里面有两个必不可少的参数,一个是 token 一个是代理页面的访问地址proxy.html 注意:这个代理页面是在统一登录页这个项目里面,所以域名一定要统一,至于是干什么用的后面会讲到。
这个时候在统一登录页面面就会通过iframe加载我们的A项目,现在在A项目里面通过window.location是能够获取到url里面携带的参数的

/**
 * 动态加载iframe
 */
export const linkApp = item => {
  const PROXY_BASEURL = process.env.VUE_APP_PROXY_BASEURL
  const OPENDEVAPP = process.env.VUE_APP_OPENDEVAPP
  const { url, id, category } = item
  let src = ''
  // 挂载全局URL,用来打开对应的网站应用
  window.OPENURL = category === '6' ? `${OPENDEVAPP}?sys=${id}` : url
  if (url.endsWith('?')) {
    src = `${url}token=${getToken()}&baseUrl=${PROXY_BASEURL}`
  } else {
    src = `${url}?token=${getToken()}&baseUrl=${PROXY_BASEURL}`
  }

  const iframe = document.createElement('iframe')
  if (category === '6') {
    iframe.setAttribute('src', `${OPENDEVAPP}?sys=${id}&token=${getToken()}&baseUrl=${PROXY_BASEURL}`)
  } else {
    iframe.setAttribute('src', src)
  }
  iframe.setAttribute('id', 'iframe-' + id)
  iframe.setAttribute('width', 0)
  iframe.setAttribute('height', 0)
  iframe.setAttribute('display', 'none')
  window.IFRAMEID = id
  document.body.appendChild(iframe)
}

3.代理页面 proxy.html

<script>
  window.onload = function() {
    window.top.getDomainValue()
  }
</script>
/**
 * 回调成功,打开要访问的应用
 */
window.getDomainValue = function() {
  // 让浏览器允许打开页面
  const tempwindow = window.open('_blank')
  tempwindow.location = window.OPENURL
  // 删除动态添加的iframe
  const elIframe = document.getElementById('iframe-' + window.IFRAMEID)
  // 清除状态
  store.dispatch('ACTION_LOADING', false)
  elIframe.remove()
}

4.梳理一下流程
用户在统一登录页里面点击某一个系统,这个时候我们会在全局window对象下面挂载这个url,比如window.openUrl=‘xxxx’,然后动态创建一个iframe去加载openUrl,这个时候其实就相当于访问了当前点击的这个应用,然后在这个应用里面会有一部分们逻辑,后面会讲到,应用里面执行完逻辑之后会通知统一登录页,然后统一登录页通过代理页面去访问到window下面挂载的一个方法,这个方法就是 getDomainValue 他的目的就是打开一个标签页去加载应用,好了下面讲一下应用这边的业务
二. 业务系统 ABCDEFG…
我们只需在应用里面加载如果也如,比如我们的项目是一个vue,那我们的主文件比如说是main.js,也就是访问应用时最先访问的文件,我们只需要在里面加入如下代码

// main.js
function getQueryString(name) {
  const reg = new RegExp('(^|&)' + name + '=([^&]*)(&|$)', 'i')
  const r = window.location.search.substr(1).match(reg)
  if (r != null) {
    return decodeURI(r[2])
  }
}

if (getQueryString('token') != undefined && getQueryString('token') != '' && getQueryString('token') != null) {
  // 代理页面地址
  const BASEURL = getQueryString('baseUrl')
  // token存取到cookie或者localStorage中
  setToken(getQueryString('token'))
  // 动态创建iframe
  let el = document.createElement('iframe');  
  el.name = 'tmp_frame';  
  el.src = BASEURL + Math.random();
  el.style.display = 'none';  
  document.body.appendChild(el);  
}

getQueryString这个方法就是用来获取url里面的token以及代理页面的地址,下面的if逻辑就是当我们拿到token之后我们就会在cookie或者localStorage里面取种token,并且在业务系统里面会动态创建一个iframe,这个iframe加载的就是我们的代理页面,那么代理页面就是去访问window.top.getDomainValue 这个方法到这整个逻辑就跑起来了
三.关于项目的优化
1.统一登录页第一次打开项目的时候会很慢?
因为我们第一次打开项目一些缓存并没有,所以会等项目加载完才回去执行里面的逻辑,解决方案可以在业务系统里面再加一个HTML 这个里面的逻辑就是获取token通知统一登录页,这个时候会很快的打开项目,加载项目的过程放在新的标签页,在加载的过程中已经在这个域下面种好了token直接获取使用即可
2.如果登录完成一个,我直接在浏览器访问另外一个项目也不想登陆怎么办(也就是不通过统一登录页)?
所有系统的登录页面都是统一的,都是用统一登录页面,比如访问B项目,token失效了,这个时候会跳转到统一登录页的登录页面,然后进行登陆,会进入项目正常执行一系列操作,如果想要登陆成功之后不进项目导航页直接进B项目,那就记录号浏览器的导航的访问记录,登陆成功之后创建iframe直接打开B项目
四.其他方案实现
iframe + postMessage通知的方式 感觉类似 比如钉钉第三方对接的扫码登录
研究中…

  • 3
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值