在跨域场景下实现单点登录和网站内容融合
source link: http://www.ibm.com/developerworks/cn/web/wa-cross-domains-sso-and-sites-integration/index.html?ca=drs-&%3Butm_source=tuicool&%3Butm_medium=referral
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
前言
在 企业 业务发生变化时,采用技术手段整合多个网站从来就不是一件轻松的事情。这里面的障碍主要有:
- 不同的账号和权限管理体系
- 不同的界面风格
- 生硬的链接跳转
- 难以复用的后端接口
从我们的经验看,以上问题要解决,需要使用一种渐进整合的架构,因为整合旧内容和生产新内容是同时进行的,我们做的事情就像是:开着飞机换引擎。
解决思路
本文介绍一种在跨域场景下实现单点登录和网站内容融合的方案,针对性解决前述 4 大障碍,其对应的主要技术点是:
- 通过单点登录统一账号
- 通过 iframe 门户页提供统一的外框导航菜单
- 通过拦截链接跳转和重置地址栏,实现统一的链接跳转行为
- 通过支持 CORS 请求协议,接口可以部署在任何一个关联应用
以下举例说明此方案。
假设我们需要整合的网站分别是 a.my.com
(简称 a )和 b.my.com
(简称 b ) 。
实现单点登录
为了实现单点登录,我们需要新增单点登录服务器 sso.my.com
。
改造 a、b 网站底层,让他们的用户账号映射为一个统一的 uniqueId
。并在底层调用单点登录服务器提供的登录认证。
用户首次访问 a 网站感知到的流程如下:
- 用户访问页面
//a.my.com/page.htm
-
a.my.com
调用sso.my.com
认证服务进行鉴权 - 鉴权失败,跳转登录页
//sso.my.com/login.htm?returnUrl=//a.my.com/page.htm
- 登录后回跳进入页面
//a.my.com/page.htm
同理,用户首次访问 b 网站,也会获得一致的体验。
下面是一个淘宝的单点登录的案例,访问"我的淘宝"页面,进入的是登录页。
原页面地址: https://i.taobao.com/my_taobao.htm
带回跳的登录页地址: https://login.taobao.com/member/login.jhtml?redirect_url=https%3A%2F%2Fi.taobao.com%2Fmy_taobao.htm
(如图 1 所示)
图 1. 带回跳的淘宝登录页地址
构建 iframe 门户页
iframe 框架曾经是早期网站内容布局的一种方式,后来因为 Ajax 能自由地进行局部刷新,iframe 的必要性下降,加上开发维护的成本较高,逐渐被减少使用,仅在部分场景使用。但本文正是充分利用 iframe 的特点:能隔离外框导航菜单和页面内容。
为了实现 iframe 门户页,我们需要新增门户服务器 portal .my.com
。
开发一个 iframe 门户页 portal.my.com/home.htm
,以后所有的页面请求,都通过它嵌套访问。
改造 a、b 网站页面的脚手架,让页面拥有两种模式:独立使用模式和嵌套使用模式,在嵌套使用模式下隐藏原有的菜单信息。
用户访问 a 网站感知到的流程如下:
- 用户访问页面
//portal.my.com/home.htm?page=//a.my.com/page.htm
- iframe 门户页根据参数呈现外框导航菜单
- iframe 门户页中的 iframe,间接访问
//a.my.com/page.htm?mode=nested
,呈现页面内容。在这个例子中,模式 (mode
)参数值nested
表示页面以嵌套的方式被访问,指示不要渲染页面原有的菜单(如有)
同理,用户首次访问 b 网站,也会获得一致的体验。
为了获得更好的体验,应该更改原有页面的主题,让他们的主题颜色等样式趋于一致,如图 2 所示:
图 2. 更改原有页面的主题
拦截链接跳转和重置地址栏
现在用户直接通过页面地址打开页面,已经能获得我们所期望的页面效果。但是页面上的链接发生点击时,会发生以下两种情况:
- 在 iframe 中跳转
- 在新窗口或新标签页打开页面
需要注意的是,页面上原有的跳转代码目前还没有去重构,所以跳转地址仍然是直接地址,而不是形如 //portal.my.com/home.htm?page=//a.my.com/page.htm
这样的封装地址。 <a>
标签可以拦截点击事件,代码中的跳转则只能重构代码,比如调用一个新的公共链接跳转方法。
我们可以制作一个公共的 JS 文件,并改造原有页面,引入这个 JS 文件以便拦截跳转。在拦截到跳转后:
history.pushState
为了获得更好的体验,我们还可以进行 URL 重写,让用户通过 REST 风格的 URL 进行访问,这时 URL 的格式是: //portal.my.com/home/page/a.my.com/page
。
图 3. 拦截链接跳转
改造后端接口
将后端接口改造为支持 CORS 请求的后端接口,这样跨域访问接口不再成为障碍,接口可以部署到任意关联应用中。
图 4 是 CORS 示意图 (来源: https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS )。与页面不在一个域的请求,即为 CORS 请求。
图 4. CORS 示意图
对一个简单的请求,没有自定义头部,要么使用 GET
,要么使用 POST
,它的 Content-Type
请求头是 text/plain
、 multipart/form-data
或者 application/x-www-form-urlencoded
,浏览器会自动添加一个名叫 Origin
的额外的头部发送 请求 。 Origin
头部包含请求页面的协议域名、端口,这样服务器可以很容易的决定它是否应该提供响应。服务器端对于 CORS 的支持,主要就是通过设置 Access-Control-Allow-Origin
响应头 来进行的。
浏览器发出的请求如清单 1 所示:
清单 1. 浏览器发出的请求
GET /api HTTP/1.1 … HOST: api.my.com Referer: http://portal.my.com/home.htm?page=//a.my.com/page.htm Origin: http://portal.my.com
一个支持 CORS 的服务可能给出如清单 2 响应:
清单 2. 一个支持 CORS 的服务响应
HTTP/1.1 200 OK Access-Control-Allow-Origin: http://portal.my.com Content-Type: application/json; charset=UTF8 … [Payload Here]
通常,如果在使用 Fetch
发送请求时,如果需要携带 cookie,还需指定 credentials 为 include
,如图 5 所示(来源: http://promincproductions.com/blog/cross-domain-ajax-request-cookies-cors/ ):
图 5. 发送携带 Cookie 的跨域请求
注意:对于上传文件等复杂请求(上传请求监听了 XMLHttpRequestUpload
以便获得上传进度),将触发浏览器发送预检请求的规则,这时需要在后端服务器正确响应 OPTIONS
请求。
总结
本方案在实现单点登录、构建 iframe 门户页、拦截链接跳转和重置地址栏、改造后端接口共 4 个方面,渐进地整合几个关联网站,最终达到一致的用户体验,不仅是看上去一致,而且为统一多个网站的底层架构埋下伏笔。
参考资源
本方案中提到了一些技术名词,如单点登录、URL 重写、CORS、重置地址栏:
- 参考 CORS ,了解 CORS 跨域资源共享协议。
- 参考 History.pushState ,了解如何重置地址栏。
- 参考 Rewrite engine ,了解如何进行 URL 重写以及可用的引擎产品。
- 查看相关文章 构建和实现单点登录解决方案 。
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK