sso统一认证postMessage无感处理

提要

SSO统一认证现在属于传统必备技能,很多人都花了很大段的话去描述这个过程,感觉不够极客,有水过程拔高的嫌疑,这里我先划分几种场景和情况

  1. 负载均衡衍生方案(seesion或者cookie固化存储方案)
  2. cas票据认证(Service Ticket,属于偏服务端的解决方案)
  3. sso一次登录多应用通行(偏前端,但其实cas也是sso实现的方案,这里区分来说,主要只现代常用)
  4. 授权登录(微信授权确认认证、第三方授权登录)
  5. 域认证方案(多见于大网区域划分,属网络安全方案)
  6. nginx同域代理方案(同源策略)

过程

当前划分的几种方式其实是随着技术路线的发展在发展的

  1. 负载均衡衍生方案 这套方案多常见于.net,因为有session固化存储进sqlserve数据库的方式,能够有效解决多个应用共享coocie或者session会话的标识,之前的场景多为单系统,因技术处理其他语言没有对应的解决方案去处理,所以应用范围多仅限于负载均衡时coocie服务端会丢失的问题,将相关的网站整站负载均衡处理,不需要改动其他代码即可完成,而且不管是服务端渲染还是前后端分离方式都能够支持,硬性要求就是服务端必须统一为C#,有极大的弊端,但优越在配置简单,破坏性弱。
  2. cas票据认证 此场景属于token认证之前的一个解决方案,在服务端去处理网站或者接口的认证,常见于前后端未分离的项目如jsp、asp但不仅限与此,前后端分离也支持,区别在于服务端为有状态服务,必须要求有一个票据生成端,各个服务端去集成相关票据服务提供的插件,其实也就是解决了跨语言的问题,如果套到token上也是可行的。
  • 优势:
    更安全的判断,更灵活的路由授权机制,那些访问路径下屏蔽认证、那些路径下需要很灵活
    相较于sssion方式不容易丢失凭证
  • 劣势
    需要单独配置票据服务
    集成客户端对一些诉求比较特殊的系统,配置复杂度较高,兼容自有逻辑时需要很密集的处理,牺牲了票据的同步性,很容易产生重复跳转,304常常伴飞
    针对前后端分离方案,可能得页面端也附加一些逻辑以满足具体要求
  1. sso一次登录多应用通行 随着前端的工程化、生态化,IE6-9的封印被解除后,人们对美的追求有点儿收不住,历史项目逐渐被清出,多数sso趋向从openid第三方授权认证找到的灵感,思路有些雷同,(有差异,免杠精)只不过把其中确认页换成了自动的,在加上无状态接口请求的盛行,人们更喜欢用token的认证形式去代替coocie或者session这种形式,更进一步放大接口分布式的能力,其实变相的是把存储由服务端放到了前端
  2. 授权登录(微信授权确认认证、第三方授权登录) 盗个图说明以下,这个授权认证相信搞过多个平台认证的都很熟悉,本质上有点儿脱胎于cas与前端处理的结合 image.png
  3. 域认证方案 域认证这个属于比较古老的方案,属于电脑机器账号密码登录,权限划分等多为电脑的域权限划分,属于pc应用的产物,(网关、网络管理员)
  4. nginx同域代理方案 能够把公共的coocie信息共享给其子服务使用,属于快速备选方案,很容易适配两个不同的系统

优化处理

从应用型我主要介绍现在sso在前端部分去处理主跳转逻辑的思路 传统的思路网上介绍很多,但绕不开一个问题

  1. token服务端没办法去存储区分token的值,也就是说,每次必须去传token,如果不传token,服务端是没办法用其他标识去识别客户端标识的(ip、机器码等都有弊端或者花销大),ip在网络环境复杂的情况下,区分内网,外网风险太大,mac地址获取客户端ActiveX插件式获取体验和兼容性问题明显。
  2. 因此前端解决的方案主要解决的一个核心问题是,保证每次都能传递token,页面参数传递这种方式,体验差且也很容易在初期服务逻辑不健全和稳定时反复跳转,问题排查艰难

基于以上的点,我们主要解决的核心问题放在了跨域时,排除url传递,如何传值的问题,因此有了postmessage跨域传值方案,且已经过线上验证,不会存在页面跳转的问题,最多只有一次自刷新逻辑。 统一认证页面下附加的token交换页,用于传递值,如果安全考量,可以把*换成指定的ip段 子应用端,(主判断逻辑最好是提供好的逻辑处理,便与后期统一调整) 由此以下的完整客户端逻辑实现如下:

function getTokenInfo(redirectUrl){
     // 注册消息事件监听,接受子元素给的数据
     window.addEventListener('message', (e) => {
       var token=e.data;
       if(token){
        localStorage.setItem('token', token);
        document.body.removeChild(document.getElementById('if'));
        //刷新
        location.reload();
       }else{
        const path=publicWay;
        const newRedirectUrl = `${path}/#${
          '/login'
        }`
        window.location.href = newRedirectUrl + '?redirect=' + redirectUrl
       }
    }, false);
   if(!document.getElementById('if')){
    var iframe = document.createElement('iframe');
        iframe.style.cssText = 'display:none;';
        iframe.src = `${publicWay}/token.html`
        iframe.height = 0
        iframe.width = 0
        iframe.id = 'if'
        document.body.appendChild(iframe) 
		
}
}
/**
 * 单点登录
 */
const loginAuth = (redirectUrl, loginedcallback) => {
  console.log('-------单点登录开始-------')
  // 用户中心根据用户获取数据接口
  var path = `${config.casPrefixUrl}/api/auth/user-info`
  let token
  const paramTokenReg = new RegExp('(^|&)token=([^&]*)(&|$)', 'i')
  const regExpMatchArray = window.location.search.substr(1).match(paramTokenReg)
  token =
    regExpMatchArray != null
      ? decodeURI(regExpMatchArray[2])
      : localStorage.getItem('token')
  token = tokenReg.test(token) ? token : '' // ;
  /** */
 // getTokenInfo
 if(!token){
  getTokenInfo(redirectUrl);
 }else{
   //有数据结果才调用
  const request = new XMLHttpRequest()
  request.open('GET', path + '?token=' + token, true)
  request.send()
  request.onreadystatechange = function() {
    // 请求成功返回数据并解析
    if (request.readyState === 4 && request.status === 200) {
      /**
       * 如果正常返回了数据,则回调处理
       * 登录成功后获写入token,仅写入有效token
       */
       if (token!=localStorage.getItem('token')) {
        localStorage.setItem('token', token);
        // const url=location.origin +location.pathname+location.hash+location.search.replace(/&?token=[^&]*/,'');
        // location.replace(url);
      }
      if (loginedcallback) {
        const { data } = JSON.parse(request.responseText)
        loginedcallback(data)
    
      }
    }
    // 未登录时,接口返回401,
    if (request.readyState === 4 && request.status === 401) {
      const path=publicWay;
      const newRedirectUrl = `${path}/#${
        '/login'
      }`
      localStorage.setItem('token','');
      window.location.href = newRedirectUrl + '?redirect=' + redirectUrl;
    }
  
  }
}
  console.log('-------单点登录结束-------')
}
复制代码

总结

技术的发展往往根据既有的规律去升级,不会凭空捏造,无中生有,实践出真理,对做技术的更是这样,你只有真正的遇到了既有的问题,剪除了其他旁支,才能得出你最期待的结果

往期目录

PS

最近又进入万金油状态,乱七八糟什么东西都在整,焦虑突从心起,无伤大雅,很快调整了,不管做什么,都是我技术路上的柴火,强大的内心不被挫败,坚持我们的代码梦想、技术YYDS!!!
---- 老哥们加油,不要被一时的挫败和人影响,我中二的标题图又来了,哈哈哈!

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值