跨域SSO的实现之一:架构设计

  翻译自CodeProject网站ASP.NET9月份最佳文章:Single Sign On (SSO) for cross-domain ASP.NET applications。

  翻译不妥之处还望大家多多指导、相互交流。

  文章分为两部分:架构设计和程序实现,此为第一篇即:架构设计或者叫设计蓝图(Part-I - The design blue print)。:)

  简介

  周一的早晨,当你正在纳闷周末咋就一眨眼过去了并对接下来漫长的一周感到无比蛋疼之时,你收到了一份Email。

  操蛋的是它既不是微软的offer也不是Google的offer,而是客户发来的一个新需求。

  他说你们现在帮我们公司做了很多的ASP.NET的网站和忽悠我们上线的各种系统,现在我想要我的客户只要在我们拥有的任何一个网站上登录一次,那么在我所有的网站上该用户就都已经登录了,同样,随便他从哪个网站上注销掉,那么他也就从我们所有的网站上注销了......

  你受不了客户这么罗嗦了,心想不就是要一个SSO功能吗?使用ASP.NET的form authentication不就可以实现了?因为这样可以在同域的不用网站下共享cookie,只需要在machineKey设置一样的配置节就可以了。放狗一搜,果然有xxxx条结果。放狗找东西可是我们程序员的特长。

  开工前,你又扫了一眼邮件,等等,你看到了邮件中的一行话,微微一蛋疼:我们部署了那些网站,但不是都在同一个域名下。

  你的客户狠狠地给你来了个下马威,好像他早就放狗搜过,因为cookie不能跨域共享,也就不能用来实现跨域验证了。

  这到底是神马一回事情!(和老外一样扯玩淡,下面正经些)

  ASP.NET中的验证原理

  这个问题可能是老生常谈了,但在解决难题之前,还是先回归基础来看一看事物的本质到底是如何的。因此,我们重温一下ASP.NET表单验证的原理也并不坏。
  下面是ASP.NET表单验证的流程图

  验证流程

  1:你访问一个需要用户验证的ASP.NET页面

  2:在此请求中ASP.NET运行时开始查找cookie(由于表单验证的cookie),如果没有查找到,那么将跳转到登录页面(登录页地址配置在了web.config文件中)

  3:在登录页面中,你提供了相关的验证凭证并点击了登录按钮,系统和已存储的数据对比验证成功后,将Thread.CurrentPrincipal.Identity.Name的属性值设置成了你提供的用户名,并在Response中写入了cookie(同时还写入了用户信息和一些如cookie名,失效日期等),并重定向到登录前的页面。

  4:当你再点击其他的页面(或者点击导航到其他的页面),浏览器发送验证的cookie(也可能包含在该网站下写入的一些其他cookie),这一次已经包含了在上一次response中上次验证获取到的cookie。

  5:和以前一样,ASP.NET运行时在请求中查找验证的cookie,这一次找到了,接下来做一些检查(如失效日期、路径等等),如果还没有失效,那么读取出它的值,恢复出用户的信息,将Thread.CurrentPrincipal.Identity.Name的属性值设置成恢复出的用户名,检查该用户是否有权限去访问当前请求的页面,如果有,那么页面执行的结果返回到用户的浏览器。

  过程很简单,对吗?

  ASP.NET中多站点同域下的验证原理

  如前所述,ASP.NET表单验证完全依赖于cookie。那么只要使得不同的站点共享同样的验证cookie,那么就可以实现在一个站点登录实现所有站点的登录。

  HTTP协议指出,如果两个站点是同域(或者是子域)的,那么可以共享cookie。本地的处理是浏览器根据网站的URL存储cookie在本地(磁盘或者内存中)。当你请求接下来的任意页面时,浏览器读取和当前请求的URL匹配的域或子域的cookies,并将此cookies包含在当前的请求中。

  现在我们假设有下面两个网站:

  www.mydomain.com/site1

  www.mydomain.com/site2

  这两个站点共享同样的主机地址(同样的域mydomain.com和子域www),且两个站点都被配置成了对用户验证和授权都使用表单验证。假设你已经登录过了站点www.mydomain.com/site1,如前所述,你的浏览器现在对于站点www.mydomain.com/site1已经有了表单验证的cookie。

  现在你随意访问以www.mydomain.com/site1开头的URL,表单验证的cookie都将被包含在请求被发送。为什么?是因为此cookie本来就属于该站点吗?对的,但不是完全正确。事实上,是因为请求的URL:www.mydomain.com/site1http://www.mydomain.com/拥有同样的域名和子域名。

  那么在你登录了www.mydomain.com/site1后,如果你点击www.mydomain.com/site2下的URL,表单验证的cookie也将被包含在请求中发送,这同样是因为www.mydomain.com/site2与站点http://www.mydomain.com/拥有同样的域名和子域名,尽管它是不一样的应用站点(site2)。显然,在拥有一样主机地址不一样的应用站点名之间是可以共享表单验证cookie的,这样就实现了一处登录处处都已经登录的功能(也就是单点登录)。

  然而,ASP.NET没有允许你仅仅通过将同主机地址下的站点部署上表单验证后就自动完成了单点登录。为什么这样呢?因为每一个不同的ASP.NET web应用程序使用它自己的密钥去加密和加密cookie(还有诸如ViewState之类的)从而确保了安全。除非你给每一个站点指定了同样的加密密钥,那么cookies将被发送,但是另一个应用站点不能够读取验证cookies的值。

  指定同样的验证密钥可以解决这个问题。为每一个ASP.NET应用站点使用同样的<machinekey>配置节即可,如下:

<machineKey 
  
validationKey="21F090935F6E49C2C797F69BBAAD8402ABD2EE0B667A8B44EA7DD4374267A75D" 
  decryptionKey
="ABAA84D7EC4BB56D75D217CECFFB9628809BDB8BF91CFCD64568A145BE59719F"
  validation
="SHA1"
  decryption
="AES"/>

操作流程:

 

  请求http://www.domain1.com/中一个需要验证的页面

状态:浏览器没有验证cookie

NET技术跨域SSO的实现之一:架构设计,转载需保留来源!

郑重声明:本文版权归原作者所有,转载文章仅为传播更多信息之目的,如作者信息标记有误,请第一时间联系我们修改或删除,多谢。