摘要
在这篇文章中,Masoud讨论了应用ASP.NET中统一身份验证模型进行跨应用程序验证的问题,包括:Membership Providers, web.config配置,配置文件的加密解密等。在文章的最后,作者提供了通过ASP.NET login controls来验证的程序。
by Masoud Tabatabaei:
目录
- 简介
- 什么是SSO?它是怎样工作的?
- 系统条件
- 工作
- 下载
- 结论
通常在你要实现ASP.NET web应用程序的身份验证时,你需要为你的每一个应用程序创建一个登录页面。想象一下,如果你有两个或者更多的互相关联的web应用程序,你可能希望通过某种机制为你的所有带关联的应用程序实现仅出现一次登录页面。这样,一旦你登录了一次,你就可以浏览所有的关联程序,而不再需要额外的登录了。单点登录(SSO)就是这样的访问控制机制,它允许一个用户通过一次验证就可以访问所有软件系统资源。
试想你在你的服务器上创建了两个或者更多的web站点。就像其他的web站点一样,你只是使用ASP.NET权限验证机制来验证你的用户。那么,你的这些站点可能需要一个或更多的登录页面。现在你正试图证明怎么样通过更改你的配置来实现跨程序登录。换句话说,我们只想给我们的程序配置一个登录页面,并且一旦用户通过了验证,他就可以浏览其他所有的站点,而不需要另外的登录。在这篇文章的附录中,你也可以看到如何加密你的配置文件。
什么是单点登录?它是怎样工作的?
在许多的公司里,他们有一些以web站点或web应用程序为表现层的系统。自然,由于安全议题他们将需要通过基于ASP.NET 2.0,通过Membership Provider 和 Role Provider 或者定制实现权限验证和权限验证系统。不论怎样,所有的站点都会默认有一个确定用户的ID和密码在数据库中是否有效的”login.aspx” web窗体.当你只有一个站点或者这些站点都是独立运行时,这样做是没有问题的。但是当你有两个或多个站点,而且站点间是关联在一起或链接在一起的,你没准就会问:为什么每个应用程序你都必须登录一次?为什么你不可以只有一个”login.aspx”来实现验证,并让所有不关联程序真正统一起来。幸运的是,在ASP.NET 2.0中你可以通过同样的配置来实现跨应用程序访问,不论是你的新的站点还是已经存在的站点。
在ASP.NET配置文件(web.config)中有一个配置节(在<system.web中)命名为<machineKey>,负责加密和解密窗体(这些窗体可以读窗体权限验证cookies)权限认证的cookie数据和view-state数据,也负责校验进程外(out-of-process)session 状态标识。所以当用户一旦被验证通过并且有一个cookie保存到了本地计算机,其他拥有同样<machineKey>配置的应用程序也可以识别此cookie为有效的权限票据。所以在其他拥有同样<machineKey>配置的应用程序中就不再需要第二次登陆了。
由于<machineKey>信息是敏感的,你需要加密配置文件中的此节信息。为了实现这个目标,我将使用ConfigurationManager类和他的方法。这里还有一个类SectionInformation,包含有配置中单个配置节的元数据。此类中有个方法ProtectSection(),用来解密你的配置文件的配置节。
系统条件
· A web server running on Windows 2000 or later
· .NET Framework 2.0
· Visual Studio 2005
· Microsoft SQL Server 2005 Express Edition
现在让我们来看看在我们的项目中发生了什么。我有一个站点(Aspalliance1)站点中包含一个登录页面”Login.aspx”.用户可以通过此页来进行权限验证。在这个站点里还有一个页面叫做”Default.aspx”,它有一个header和一些文本另外还有一个到Aspalliance2站点的链接。你将会看到一旦这个用户登录了,他可以导航到其他站点而不需要第二次登陆。这里还有一个安置有两个加密和解密的按钮的页面”Encryption.aspx”,用来加密和解密配置文件。
就像我之前所说的那样,你可以通过在你的web配置文件中一点点小小的配置实现跨应用程序访问。在web.config文件中,有一个名为<system.web>的配置节。我们将对<system.web>做相同的配置,只需要将配置节<machineKey>和它的值放到<system.web>配置节中。<machineKey>有一些属性,我将要去配置他们。首先,就是指定用来验证的加密类型。validationKey 定义了用来验证解密数据的key,decryptionKey定义了用来加密和解密的数据的key,抑或是key生成的过程。
清单 1: 配置web.config中的machineKey
<machineKey
validationKey="282487E295028E59B8F411ACB689CCD6F39DDD21E6055A3EE480424315994760ADF
21B580D8587DB675FA02F79167413044E25309CCCDB647174D5B3D0DD9141"
decryptionKey="8B6697227CBCA902B1A0925D40FAA00B353F2DF4359D2099"
validation="SHA1"/>
这个样例代码并没有被加密,并且它不会被发布到服务器上。因为处于安全考虑,发布到服务器的<machineKey>的加密是非常重要的。你可以在清单2中看到加密后的<machineKey>。
清单 2: web.config 中加密后的machineKey
<machineKeyconfigProtectionProvider="RsaProtectedConfigurationProvider">
<EncryptedDataType="http://www.w3.org/2001/04/xmlenc#Element"
xmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethodAlgorithm="http://www.w3.org/2001/04/xmlenc#tripledes-cbc" />
<KeyInfoxmlns="http://www.w3.org/2000/09/xmldsig#">
<EncryptedKeyxmlns="http://www.w3.org/2001/04/xmlenc#">
<EncryptionMethodAlgorithm="http://www.w3.org/2001/04/xmlenc#rsa-1_5" />
<KeyInfoxmlns="http://www.w3.org/2000/09/xmldsig#">
<KeyName>Rsa Key</KeyName>
</KeyInfo>
<CipherData>
<CipherValue>
lm3mfPX/94Zm3HgdbsmKiIxbrWM14t3/ugxs40BFOAHbIaCtwQ3gVQusFtOFVUoNVny01kgBCeh10rVEId
djNZ/8luBNoCbHm8OLjgPLHVrT+G0c/LRpESJk2ni/Jy2sWKXlgejgSQ1W5NE53GZtG3s9hu+nk4OWxntS
6z3v7AM=
</CipherValue>
</CipherData>
</EncryptedKey>
</KeyInfo>
<CipherData>
<CipherValue>
BCEGUV/dh1Imbcm5vn0Kn8NrD+EX+KemenR7x+VekwT1ZO6y5+jRyF4RDWMJCfJ1jHC36+MAfCdHuXN0rP
B6hu5YUtX9VA5q5N0NGrs9AIpG+0ihuuS3HDzQe3P6nlI30m1h0pmL1yJBovY0i6fbCA6++GT2MdwCLERk
+PVWmoq7p1q97n5pNzNqhVKCX45lhS5ySVS+MjJXVeTrcatftpvaUcjLsNcL2kMerzf5w/SU3AbLEuY04w
dgYWX5tWzxqeUcghdlWLD0tQi8qyyfVfzXPYozR5sspWHdgqmAycrACHN2dcONWPjT4BanRWb1ouKuP8K+
0CEFE/Hj2ChpYw==
</CipherValue>
</CipherData>
</EncryptedData>
</machineKey>
你可以通过Configuration、SectionInformation两个类来加密你的配置文件。为了加密和解密你的<machineKey>让我们来写一些代码吧。SectionInformation类有一个方法ProtectSection(),可以得到一个描绘Protection Provider的字符串比如"RSAProctedConfigurationProvider",并且加密这个配置节。这里还有一个Boolean类型的属性ForceSave,当需要配置类的save方法保存配置文件时需要将它设置为true。这里有"Encryption.aspx"页面的代码,页面中包含有两个按钮来加密和解密配置文件。
清单 3:web配置文件的加密代码
protected void btnEncrypt_Click(object sender, EventArgs e)
{
try
{
Configuration config = WebConfigurationManager.OpenWebConfiguration(
"/Aspalliance1 ");
ConfigurationSection machineKeySection = config.GetSection(
"system.web/machineKey");
machineKeySection.SectionInformation.ProtectSection(
"RSAProtectedConfigurationProvider");
machineKeySection.SectionInformation.ForceSave = true;
config.Save();
Response.Write("<h2 style='color:red'>Encryption Succeed</h2>");
}
catch (Exception ex)
{
Response.Write("<h2 style='color:red'>Error while encrypting</h2><br/>");
Response.Write(ex.Message);
}
}
清单 4: web配置文件的解密代码
protected void btnDecrypt_Click(object sender, EventArgs e)
{
try
{
Configuration config = WebConfigurationManager.OpenWebConfiguration(
"/Aspalliance1 ");
ConfigurationSection machineKeySection = config.GetSection(
"system.web/machineKey");
machineKeySection.SectionInformation.UnprotectSection();
machineKeySection.SectionInformation.ForceSave = true;
config.Save();
Response.Write("<h2 style='color:red'>Decryption Succeed</h2>");
}
catch (Exception ex)
{
Response.Write("<h2 style='color:red'>Error while decrypting</h2><br/>");
Response.Write(ex.Message);
}
}
现在你必须在这个站点中设置相同的配置。首先你需要更改你的窗体验证部分的loginUrl,这个窗体将被用来将匿名用户跳转到”Login.aspx”页。只是,现在它将把用户重定向到Aspalliance1站点中的”Login.aspx”页。
清单 5: 设置 web.config中的验证节
<authentication mode="Forms">
<forms loginUrl="http://localhost/Aspalliance1/login.aspx"name=".ASPXAUTH"/>
</authentication>
如果你想实现跨程序登录你的好多站点时,最重要的一点就是你必须把你的两个或更多的站点配置为相同的<machineKey>。所以我只需要拷贝并粘贴Aspalliance1 站点中的<machineKey>配置节到Aspalliance2站点。现在都已经准备好了,你可以测试你的站点了。
清单 6: 设置web.config 中的 machineKey
<machineKey
validationKey="282487E295028E59B8F411ACB689CCD6F39DDD21E6055A3EE480424315994760ADF
21B580D8587DB675FA02F79167413044E25309CCCDB647174D5B3D0DD9141"
decryptionKey="8B6697227CBCA902B1A0925D40FAA00B353F2DF4359D2099"
validation="SHA1"/>
[下载]
测试这个站点的话,可以使用用户名:Admin密码:123456&来登录。
这个下载附件中有一个VS 2005项目,其中包含有两个站点:aspalliance1 and aspalliance2.
要安装这个实例的话,你需要创建两个IIS虚拟目录命名为:aspalliance1 和 aspalliance2,并将地址指向相应的文件夹。你也可以通过Visual Studio 2005打开站点。
当用户要交叉访问你的多个站点时,他必须重复登陆实在是麻烦。所以,如果只让用户登录一次,那会是非常棒的。实现这些,你只需要给你的"web.config" 文件增加具有相同值的<machineKey>配置。并且处于安全考虑,我建议你加密这个配置节。这个加密方法在SectionInformation类中通过ProtectSection()方法被重写了。
前面说到了文章中的方案还只能实现同一域下的不同虚拟目录的单点登录,还不能实现同一主域的单点登录,那么怎么样才能在这个方案基础上实现同一主域的不同站点的单点登录呢?
文章的核心部分在于让每个站点的MachineKey保持一致,采用Form验证,这样可以保证每台服务器Cookie加密解密的结果是一致的。Form验证将登录后的授权凭证加密后保存在Cookie中,由于同一域下面的虚拟目录,Cookie是可以共享的,因此可以同一域内直接实现单点登录,而对于不同域,Cookie是不能直接共享的,所以对于不同域而同一主域的情况,我们还需要将Cookie的domain设为主域。那么还以前文的A、B站点为例,要实现单点,我们只要在web.config中,配置authentication /forms节点下domain值为主域,如下:
<authentication mode="Forms"> <forms loginUrl="login.aspx" name=".ASPXAUTH" domain="joycode.com"/> </authentication>
即可实现同一主域不同子域站点之间共享登录了。