单点登录系统设计

363 阅读2分钟

项目背景

  • 公司各个小系统,小站点实在是有点多,每次都要输账号密码。有点不方便,于是搞起了单点登录

系统构成

  • 单点登录管理后台,用于配置单点系统用户(内部用户)关联哪些子站点的用户(外部用户)
  • 前台页面。用于给内部用户登录,登录后,可以查看配置的站点,点击跳转,可以直接登录
  • 两个系统都使用vue搭建

管理后台

  • 没啥技术点,增删改查,感谢若依框架,直接梭哈

前端页面

  • 该版本为生产版本,下面版本上线后体验差。故优化
  • 核心代码使用url拼接参数+进行路由拦截形式,因为postmessage方式,在接收的时候,一般都是在路由初始化后,所以无法做到合理的拦截。使用url方式,可拦截ticket

未命名文件 (2).jpg

该版本存在异步问题。所以优化成上面的版本

  • 情景1
    用户先到单点系统,输入账号密码登录(内部用户),登录成功后(后端会给我们一个ticket,存在本地),可以拿到后台配置的子站点信息, 点击要登录的系统,核心功能点(我们将拿到的ticket。通过iframe和postMessage的方式,跨域给到子站点,子系统进行监听后写入localStroage中),跳转到子站点,这时候能拿到ticket,去调接口,成功后,重写子系统的token。登录成功。
  • 情景2
    用户先到子系统,这时候子系统发现,ticket为空或者过期,且token为空或者过期,跳转到单点系统,单点系统如果是登录状态,同情景1,给跳转过来的子系统写ticket。两个情景不同点,判断document.referre。
  • 难点,用跨域写数据的方式,可能存在异步问题。例如,我们写过去的时候,页面跳转过去,这时候还未写成功,子系统判断ticket为空,又回到单点。如此反复。出现bug,解决 =》 在子系统使用延时器的方式去拿ticket、可能有点low,但是暂时解决了自己的问题。

核心代码,子系统监听message

 // 绑定一个事件监听器,当事件被触发时,把接收到的ticket数据写入localStorage
    window.addEventListener(
      "message",
      function (event) {
        if (!!event.data.ticket) {
          localStorage.setItem("ticket", event.data.ticket);
          localStorage.setItem("url", event.data.url);
        }
      },
      false
    );

单点系统给子系统写入ticket

 var ticket = localStorage.getItem("token");

      // 动态创建一个不可见的iframe,在iframe中加载一个跨域HTML
      var iframe = document.createElement("iframe");
      iframe.src = url;
      iframe.setAttribute(
        "style",
        "position:absolute;width:0px;height:0px;left:-500px;top:-500px;"
      );
      document.body.append(iframe);
      // 使用postMessage()方法将token传递给iframe

      iframe.onload = () => {
        iframe.contentWindow.postMessage(
          {
            ticket,
            url,
          },
          url
        );

        setTimeout(function () {
          iframe.remove();
        }, 5000);
        setTimeout(function () {
          window.open(url, "_self");
        }, 1000);

大致流程如上,各个子系统和单点间的跳转,存在问题,例如在无痕模式下,写入ticket会被清除。初版刚上,后期需要测试优化。仅做记录 that's all