SSO解决方案大全SingleSign-On1/9SSO解决方案大全SingleSign-Onforeveryone前段时间为我们的系统做SSO(单点登录)参考了很多资料,其中包括博客园二级域名的登录.翻译本文是由于作者的一句话:思想都是一样的,只不过实现起来需要创造性思维.SingleSign-On(SSO)是近来的热门话题.很多和我交往的客户中都有不止一个运行在.Net框架中的Web应用程序或者若干子域名.而他们甚至希望在不同的域名中也可以只登陆一次就可以畅游所有站点.今天我们关注的是如何在各种不同的应用场景中实现SSO.我们由简到繁,逐一攻破.1.虚拟目录的主应用和子应用间实现SSO2.使用不同验证机制实现SSO(usernamemapping)3.同一域名中,子域名下的应用程序间实现SSO4.运行在不同版本.NET下的应用程序间实现SSO5.两个不同域名下的Web应用程序间实现SSO6.混合身份验证方式模式(FormsandWindows)下实现SSO1.虚拟目录的主应用和子应用之间实现SSO假设有两个.Net的Web应用程序-Foo和Bar,Bar运行在Foo虚拟目录的子目录().二者都实现了Forms认证.实现Forms认证需要我们重写Application_AuthenticateRequest,在这个时机我们完成认证一旦通过验证就调用一下FormsAuthentication.RedirectFromLoginPage.这个方法接收的参数是用户名或者其它的一些身份信息.在Asp.net中登录用户的状态是持久化存储在客户端的cookie中.当你调用RedirectFromLoginPage时就会创建一个包含加密令牌FormsAuthenticationTicket的cookie,cookie名就是登录用户的用户名.下面的配置节在Web.config定义了这种cookie如何创建:authenticationmode=Formsformsname=.FooAuthprotection=Alltimeout=60loginUrl=login.aspx//authenticationauthenticationmode=FormsSSO解决方案大全SingleSign-On2/9formsname=.BarAuthprotection=Alltimeout=60loginUrl=login.aspx//authentication比较重要的两个属性是name和protection.按照下面的配置就可以让Foo和Bar两个程序在同样的保护级别下读写Cookie,这就实现了SSO的效果:authenticationmode=Formsformsname=.SSOAuthprotection=Alltimeout=60loginUrl=login.aspx//authentication当protection属性设置为All,通过Hash值进行加密和验证数据都存放在Cookie中.默认的验证和加密使用的Key都存储在machine.config文件,我们可以在应用程序的Web.Config文件覆盖这些值.默认值如下:machineKeyvalidationKey=AutoGenerate,IsolateAppsdecryptionKey=AutoGenerate,IsolateAppsvalidation=SHA1/IsolateApps表示为每个应用程序生成不同的Key.我们不能使用这个.为了能在多个应用程序中使用相同的Key来加密解密cookie,我们可以移除IsolateApps选项或者更好的方法是在所有需要实现SSO的应用程序的Web.Config中设置一个具体的Key值:machineKeyvalidationKey=F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902decryptionKey=F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923ACvalidation=SHA1/如果你使用同样的存储方式,实现SSO只是改动一下Web.config而已.2.使用不同认证机制实现SSO(usernamemapping)要是FOO站点使用database来做认证,Bar站点使用MembershipAPI或者其它方式做认证呢?这种情景中FOO站点创建的cookie对Bar站点毫无用处,因为cookie中的用户名对Bar没有什么意义.要想cookie起作用,你就需要再为Bar站点创建一个认证所需的cookie.这里你需要为两个站点的用户做一下映射.假如有一个Foo站点的用户JohnDoe在Bar站点需要识别成johnd.在Foo站带你你需要下面的代码:FormsAuthenticationTicketfat=newFormsAuthenticationTicket(1,johnd,DateTime.Now,DateTime.Now.AddYears(1),true,);SSO解决方案大全SingleSign-On3/9HttpCookiecookie=newHttpCookie(.BarAuth);cookie.Value=FormsAuthentication.Encrypt(fat);cookie.Expires=fat.Expiration;HttpContext.Current.Response.Cookies.Add(cookie);FormsAuthentication.RedirectFromLoginPage(JohnDoe);为了演示用户名硬编码了.这个代码片段为Bar站点创建了令牌FormsAuthenticationTicket,这时令牌里的用户名在Bar站点的上下文中就是有意义的了.这时再调用RedirectFromLoginPage创建正确的认证cookie.上面的例子你统一了了Forms认证的cookie名字,而这里你要确保他们不同--因为我们不需要两个站点共享相同的cookie:authenticationmode=Formsformsname=.FooAuthprotection=Alltimeout=60loginUrl=login.aspxslidingExpiration=true//authenticationauthenticationmode=Formsformsname=.BarAuthprotection=Alltimeout=60loginUrl=login.aspxslidingExpiration=true//authentication现在当用户在Foo站点登录,他就会被映射到到Bar站点的用户并同时创建了Foo和Bar两个站点的认证令牌.如果你想在Bar站点登录在Foo站点通行,那么代码就会是这样:FormsAuthenticationTicketfat=newFormsAuthenticationTicket(1,JohnDoe,DateTime.Now,DateTime.Now.AddYears(1),true,);HttpCookiecookie=newHttpCookie(.FooAuth);cookie.Value=FormsAuthentication.Encrypt(fat);cookie.Expires=fat.Expiration;HttpContext.Current.Response.Cookies.Add(cookie);FormsAuthentication.RedirectFromLoginPage(johnd);同样要保证两个站点的Web.config的machineKey配置节有相同的加密和解密的Key!3.同一域名中,各子域名下应用程序间实现SSO要是这样的情况又将如何:FooBar两个站点运行在不同的域名下:上面的代码又不起作用了:因为cookie会存储在不同的文件中,各自的cookieSSO解决方案大全SingleSign-On4/9对其它网站不可见.为了能让它起作用我们需要创建域级cookie,因为域级cookie对子域名都是可见的!这里我们也不能再使用RedirectFromLoginPage方法了,因为它不能灵活的创建域级cookie我们需要手工完成这个过程!FormsAuthenticationTicketfat=newFormsAuthenticationTicket(1,johnd,DateTime.Now,DateTime.Now.AddYears(1),true,);HttpCookiecookie=newHttpCookie(.BarAuth);cookie.Value=FormsAuthentication.Encrypt(fat);cookie.Expires=fat.Expiration;cookie.Domain=.foo.com;HttpContext.Current.Response.Cookies.Add(cookie);FormsAuthenticationTicketfat=newFormsAuthenticationTicket(1,JohnDoe,DateTime.Now,DateTime.Now.AddYears(1),true,);HttpCookiecookie=newHttpCookie(.FooAuth);cookie.Value=FormsAuthentication.Encrypt(fat);cookie.Expires=fat.Expiration;cookie.Domain=.foo.com;HttpContext.Current.Response.Cookies.Add(cookie);注意cookie.Domain=.foo.com;注意这一行.这里明确指定了cookie的域名为.foo.com,这样我们就保证了cookie对以及其它子域名都是可见的.(译者注:cookie的域名匹配规则是从右到左).你可以通过设置Bar站点的认证cookie的域名为bar.foo.com.这样对于其它子域名的站点它的cookie也是不可见的,这样安全了.注意RFC2109要求cookie前面有两个周期所以我们添加了一个过期时间.(cookie值实际上是一个字符串,各参数用逗号隔开).再次提醒,这里还是需要统一一下各个站点的Web.config的machineKey配置节的Key值.这种解决方案只有一种异常的情况,且看下节详解.4.运行在不同版本.Net下应用程序间实现SSO要是Foo和Bar站点运行在不同的.Net环境中上面的例子都行不通.这是由于Asp.net2.0使用了不同于1.1的加密算法:1.1版本使用的是3DES,2.0是AES.万幸,Asp.net2.0中有一个属性可以兼容1.1:SSO解决方案大全SingleSign-On5/9machineKeyvalidationKey=F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902decryptionKey=F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923ACvalidation=SHA1decryption=3DES/设置decryption=3DES就会让ASP.NET2.0使用旧版本的加密算法使cookie能够正常使用.不要企图在Asp.net1.1的Web.config文件中添加这个属性,那会报错.5.两个不同域名下的应用程序实现SSO我们已经成功的创建了可以共享的认证Cookie,但是如果Foo站点和Bar站