Duwamish 7.0 系列分析文章

原创 2006年05月19日 11:00:00
Duwamish架构分析篇
DuwamishMicrosoft提供一个企业级的分布式系统架构,如果开发企业级的分布式系统,可以模仿这种架构,如果是开发一些简单的系统,则完全可以简化。
 
以前也学习过Duwamish范例,只是发现不同时间,不同经历,有不同的体会。正如卢彦所说的一样:通过研究Duwamish示例,高手能够领悟到.Net应用架构的设计思想,低手能够学习到.Net的编程技巧,实在是老少皆宜。
 
因此,这里再次学习并体验一次Duwamish范例。
 
1Duwamish 7.0 结构分为四个逻辑层(FROM MSDN):
Web Presentation
Web 层为客户端提供对应用程序的访问。这一层是作为 Duwamish.sln 解决方案文件中的 Web 项目实现的。Web 层由 ASP.NET Web 窗体和代码隐藏文件组成。Web 窗体只是用 HTML 提供用户操作,而代码隐藏文件实现各种控件的事件处理。
业务外观层 Business Facade
业务外观层为 Web 层提供处理帐户、类别浏览和购书的界面。这一层是作为 Duwamish.sln 解决方案文件中的 BusinessFacade 项目实现的。业务外观层用作隔离层,它将用户界面与各种业务功能的实现隔离开来。除了低级系统和支持功能之外,对数据库服务器的所有调用都是通过此程序集进行的。
业务规则层 Business Rules
业务规则层是作为 Duwamish.sln 解决方案文件中的 BusinessRules 项目实现的,它包含各种业务规则和逻辑的实现。业务规则完成如客户帐户和书籍订单的验证这样的任务。
数据访问层 Data Access
数据访问层为业务规则层提供数据服务。这一层是作为 Duwamish.sln 解决方案文件中的 DataAccess 项目实现的。
 
除了上述四个逻辑层外,Duwamish 7.0 还包含封装在 Duwamish.sln 解决方案文件中的 Common 项目内的共享函数。“通用”(Common) 层包含用于在各层间传递信息的数据集。Common 项目还包含 Duwamish.sln 解决方案文件中的 SystemFramework 项目内的应用程序配置和跟踪类。
 
2,各个逻辑层之间的关系图(FROM MSDN)及其调用Sequeance图示例:


下面是Categories.aspx web页面获取CategoryDescription的整个调用过程。
1)实例化ProductSystem对象
2)调用ProductSystemGetCategories()方法
3)检测参数的合法性
4)创建Categories::DataAccess对象实例
5)返回上述对象
6)调用Categories::DataAccess对象的GetCategories()方法
7)创建CategoryData::Common对象实例
8)返回上述对象
9)返回CategoryData::Common对象实例,该实例中已经包含了需要的数据
10)返回CategoryData::Common对象实例给web/Client
11)检测数据的合法性
12)读取并显示结果:CategoryDescription



SystemFramework项目包含一些application需要的配置参数,ApplicationLog日志类和ApplicationAssert参数校验类。SystemFramework项目为所有其他的项目所引用。
 
Common项目包含了用于在各层间传递信息的数据集,如上述的CategoryData继承System.Data.DataSet,既不是所谓的typed DataSet,也不是一般的DataSet,不过简单实用,这是基于.Net Remoting开发分布式系统用来tiertier之间交互数据的一种方法。Common项目也被其他的项目引用,SystemFramework项目除外。
 
BusinessFacade项目中所有的Classes继承MarshalByRefObject class,显然是让准备将BusinessFacade tier部署为Remote Objects。不过,实际上默认这里并没有将其部署为Remote ObjectsWeb层仍然调用本地对象(《Duwamish部署方案篇》将分析这个问题)。
 
3Summary
 
在开发基于.Net Framework企业级分布式系统时,上述架构值得推荐,但也并非完美无暇,实际上存在一些值得改进的地方。显然,不可能一个范例适合所有的实际情况么,要求太苛刻了。其实,Enterprise Samples中的另外一个范例Fitch and Mather 7.0,其架构和Duwamish就有些不同了。
 
如果是开发本地的系统,就不要模仿Duwamish架构(看看上面获取CategoryDescription调用过程就知道了,太费劲。),如Business FacadeBusiness RulesClasses应采用fine-grained interface设计,层与层之间的交互参数也不必全部采用DataSet,适当的时候采用setter/getter就可以了,这样不仅可以提高开发效率,而且有助于提高performance, maintainability and reusability
 
 
References:
2, MSDN, Duwamish
Duwamish部署方案篇
 
Duwamish 7.0 支持两种多计算机部署方案。非分布式部署方案在一台 Web 主机上部署 Web 层、业务外观、业务规则和数据访问层,但可以在群集间复制该 Web 主机以达到负载平衡。分布式方案在单独的服务器上部署特定的组件。例如,业务外观、业务规则和数据访问层可能位于独立于 Web 主机的服务器上。在实际部署中数据库服务器通常位于单独的计算机上。
 
1, 非分布式部署方案
在一台 Web 主机上部署 Web 层、业务外观、业务规则和数据访问层,然后通过软件(如Application Center 2000)或硬件来实现网络场(Web Farm)内各个Web Server的负载平衡。
 
在本机默认安装Duwamish 7.0时,是采用非分布式部署方案。
  
2, 分布式部署方案
使用 .NET Framework 远程处理技术将应用程序分布到多台计算机中。简单而言,就是IIS Web ServerApplication Server分离,其中Web层(包括SystemFrameworkCommon项目)部署在IIS Web上,BusinessFacde/BusinessRules/DataAccess层(包括SystemFrameworkCommon项目)一起部署在Application Server上。
 
 
Duwamish 7.0 使用 HTTP/二进制而不是 HTTP/SOAP。使用 HTTP 的决定基于要通过端口 80 上的防火墙的要求。使用二进制而不是 SOAP 的决定基于性能上的考虑。对于大的数据块,二进制的性能优于 SOAP。因此,如果要传递大的数据块(例如,数组、数据集或数据表),则使用二进制格式化程序。如果要传递小的数据块,则选择使用 SOAP 还是二进制格式化程序是无关紧要的。传递整数时两者的性能都很好。
 
3, 如何将Duwamish 7.0部署为基于.Net Remoting的分布式系统
下面采用Microsoft提供的Deploytool工具自动进行(其实手工也很方便):
C:/Program Files/Microsoft Visual Studio .NET 2003/Enterprise Samples/Duwamish 7.0 CS>deploytool deploy RemoteMachine=localhost path="C:/Program Files/Microsoft Visual Studio .NET 2003/Enterprise Samples/Duwamish 7.0 CS/Duwamish7_Remote" 
command line窗口输入上述命令行代码。
 
[10/29/2004 6:43:43 AM] Creating directory C:/Program Files/Microsoft Visual Studio .NET 2003/Enterprise Samples/Duwamish 7.0 CS/Duwamish7_Remote on W1MIS38
[10/29/2004 6:43:43 AM] Stopping all internet services on W1MIS38
[10/29/2004 6:43:59 AM] Deploying Duwamish7 Business Facade on W1MIS38
[10/29/2004 6:43:59 AM] Creating web site on W1MIS38
[10/29/2004 6:44:00 AM] Generating remoting configuration files
[10/29/2004 6:44:00 AM] Starting all internet services on W1MIS38
[10/29/2004 6:44:02 AM] Starting Default Web Site on W1MIS38
[10/29/2004 6:44:02 AM] Deployment successful
 
运行结果:
1)在IIS创建中创建Web ApplicationDuwamish7_Facade),本地路径为:C:/Program Files/Microsoft Visual Studio .NET 2003/Enterprise Samples/Duwamish 7.0 CS/Duwamish7_Remote/web 作为Remote Server端,Bin目录下是BusinessFacde/BusinessRules/DataAccess层(包括SystemFrameworkCommon项目)DLL文件。
其中web.config文件中包含所有Remote Objects的配置,如
<wellknown mode="Singleton" type="Duwamish7.BusinessFacade.ProductSystem, Duwamish7.BusinessFacade" objectUri="ProductSystem.rem" />
 
2Web层创建remotingclient.cfg配置文件,对Application Server而言,Web层相当与Client端。
remotingclient.cfg配置文件中包含formatter的设置(binary ),选择二进制格式化程序来序列化消息,注意是出于性能的考虑。
 
3Web application加载remotingclient.cfg配置文件
Web applicationglobal.asax文件包括如下代码,在Application_OnStart事件中加载Retmoting配置文件。
void Application_OnStart()
{
ApplicationConfiguration.OnApplicationStart(Context.Server.MapPath( Context.Request.ApplicationPath ));
string configPath = Path.Combine(Context.Server.MapPath( Context.Request.ApplicationPath ),"remotingclient.cfg");
if(File.Exists(configPath))
    RemotingConfiguration.Configure(configPath);
}
 
其中前面代码ApplicationConfiguration.OnApplicationStart()是调用Duwamish7.SystemFramework.ApplicaitonConfigurationOnApplicationStart()方法,用来初始化application root和读取web.config中的配置信息(将在Duwamish代码分析篇》中进行具体分析)。
 
Reference:
1, MSDN, Duwamish 7.0
Duwamish代码分析篇
 
Written by: Rickie Lee
Nov. 02, 2004
 
继续前面的2POSTDuwamish架构分析篇》Duwamish部署方案篇》,这里在代码层次上分析Duwamish 7.0范例,主要目的是解析Duwamish范例中值得推荐的编码风格和提炼出可以重用的代码或Class
 
1,读取配置文件类-SystemFramework/ApplicationConfiguration.cs
ApplicationConfiguration类用来读取web.config文件中自定义section的配置信息,初始化一些基本设置。
ApplicationConfiguration类实现IconfigurationSectionHandler接口,并需要实现[C#]
object Create(
   object parent,
   object configContext,
   XmlNode section
)方法,以分析配置节的 XML。返回的对象被添加到配置集合中,并通过 GetConfig 访问。
 
 
部分代码片断解释:
1Code Snippet 1 – ApplicationConfiguration. OnApplicationStart()方法
public static void OnApplicationStart(String myAppPath)
{
    appRoot = myAppPath;
    System.Configuration.ConfigurationSettings.GetConfig("ApplicationConfiguration");
    System.Configuration.ConfigurationSettings.GetConfig("DuwamishConfiguration");
    System.Configuration.ConfigurationSettings.GetConfig("SourceViewer");      
}
ConfigurationSettings 类还提供了一个公共方法ConfigurationSettings.GetConfig() 用于返回用户定义的配置节的配置设置,传入的参数section name,如"ApplicationConfiguration",表示要读取的配置节。
 
NameValueCollection nv=new NameValueCollection();
//实例化NameValueCollection 类对象
nv=(NameValueCollection)ConfigurationSettings.GetConfig("ApplicationConfiguration ");
//返回用户定义的配置节的设置
return nv["SystemFramework.Tracing.Enabled"].ToString();
//返回特定键值,如SystemFramework.Tracing.Enabled
 
不过,ConfigurationSettings.GetConfig()方法在调用时,自动调用Create()方法,可以看到ApplicationConfiguration.Create()方法正是用来读取指定section的配置,并初始化设置参数。
 
Global.asax Application_OnStart 事件处理程序向 SystemFramework ApplicationConfiguration OnApplicationStart 方法发出调用,正是上述的代码片断。
 
2Code Snippet 2 Global.asaxApplication_OnStart()方法
void Application_OnStart()
{
    ApplicationConfiguration.OnApplicationStart(Context.Server.MapPath( Context.Request.ApplicationPath ));
    string configPath = Path.Combine(Context.Server.MapPath( Context.Request.ApplicationPath ),"remotingclient.cfg");
    if(File.Exists(configPath))
        RemotingConfiguration.Configure(configPath);
}
该方法肩负二大任务:(1)调用ApplicationConfiguration.OnApplicationStart()方法,并传入application的根目录(Root Directory)。(2)检测Client端的remoting配置文件是否存在(其实是web server端),如果存在,则读取并初始化remoting配置信息,如配置通道Channel等等,详见Duwamish部署方案篇
 
2,读取web.configDuwamish相关的一些配置-Common/DuwamishConfiguration.cs
Common/DuwamishConfiguration.cs也实现IconfigurationSectionHandler接口,与SystemFramework/ApplicationConfiguration.cs类相似。
 
DuwamishConfiguration配置节包括如下一些配置信息:
Database connection stringDatabase连接串)Duwamish.DataAccess.ConnectionString,是否允许页面缓存Duwamish.Web.EnablePageCache,页面缓存过期时间Duwamish.Web.PageCacheExpiresInSeconds,是否允许SSL连接Duwamish.Web.EnableSsl等等。
 
如上所述,调用DuwamishConfiguration Class 是由SystemFramework/ApplicationConfiguration.csOnApplicationStart()方法完成的:
System.Configuration.ConfigurationSettings.GetConfig("DuwamishConfiguration");
 
看看页面缓存配置在web page中如何使用的(web/book.aspx.cs文件为例):
//
// If everything succeeded, then enable page caching as indicated
// by the current application configuration.
//
if ( DuwamishConfiguration.EnablePageCache )
{
    //Enable Page Caching...
    Response.Cache.SetExpires ( DateTime.Now.AddSeconds(DuwamishConfiguration.PageCacheExpiresInSeconds));
    Response.Cache.SetCacheability(HttpCacheability.Public);
}
Page_Load事件中最后判断是否允许页面缓存。
 
3,验证数据合法性类-SystemFramework/ApplicationAssert.cs
SystemFramework/ApplicationAssert.cs Class用来进行错误检测,并调用SystemFramework/ApplicationLog.cs Class记录错误日志。
 
学习其中的部分代码片断:
1Code Snippet 1 – Check Method
[ConditionalAttribute("DEBUG")]
public static void Check(bool condition, String errorText, int lineNumber)
{
    if ( !condition )
    {
        String detailMessage = String.Empty;
        StringBuilder strBuilder;
        GenerateStackTrace(lineNumber, out detailMessage);
        strBuilder = new StringBuilder();
        strBuilder.Append("Assert: ").Append("/r/n").Append(errorText).Append("/r/n").Append(detailMessage);
        ApplicationLog.WriteWarning(strBuilder.ToString());
        System.Diagnostics.Debug.Fail(errorText, detailMessage);
    }
}
 
[ConditionalAttribute("DEBUG")]定义Check()方法为conditional method,如果预处理符号(preprocessor symbol)没有定义,compiler不仅忽略该方法,而且忽略对该方法的调用,和#if DEBUG / #else / #endif有些类似。
 
该方法用来判断条件condition是否为true,如果为false,则调用SystemFramework/ApplicationLog.WriteWarning()方法记录错误日志。
 
2Code Snippet 2 – CheckCondition Method
public static void CheckCondition(bool condition, String errorText, int lineNumber)
{
    //Test the condition
    if ( !condition )
    {
        //Assert and throw if the condition is not met
        String detailMessage;
        GenerateStackTrace(lineNumber, out detailMessage);
        Debug.Fail(errorText, detailMessage);
 
        throw new ApplicationException(errorText);
    }
}
 
该方法一般用来在进行前置条件判断,如conditionfalse,则抛出exception
 
4log日志类-SystemFramework/ApplicationLog.cs
ApplicationLog 类实现 Duwamish 7.0 中的记录和跟踪。Web.Config 文件中的配置设置确定是输出到 EventLog 文件、跟踪日志文件还是两者。下面是 Web.Config 文件中的 <ApplicationConfiguration> 节,它指定 EventLog 设置:
 
<ApplicationConfiguration>
    <!-- Event log settings -->
    <add key="SystemFramework.EventLog.Enabled" value="True" />
    <add key="SystemFramework.EventLog.Machine" value="." />
    <add key="SystemFramework.EventLog.SourceName" value="Duwamish7" />
   
    <add key="SystemFramework.EventLog.LogLevel" value="1" />
    <!-- Use the standard TraceLevel values:
             0 = Off
             1 = Error
             2 = Warning
             3 = Info
             4 = Verbose -->
Web.Config 文件的同一节还指定跟踪配置。Duwamish 7.0 跟踪日志的默认位置是:[安装 Visual Studio .NET 的驱动器号]:/Program Files/Microsoft Visual Studio .NET 2003/Enterprise Samples/Duwamish 7.0 CS/Web/DuwamishTrace.txt
 
在实际的应用系统开发中,用来提供Log功能的类应该比这个ApplicationLog类要好,这样就不去分析了,如Microsoft Exception Management Application Block就不错。
 
5Web.config配置文件-使用Web.congfig文件存储application设置
Duwamish 7.0 通过使用 Forms 身份验证来实现安全性。Forms 身份验证将未经授权的用户重定向到Web 窗体,该窗体提示用户输入其电子邮件地址和密码。
 
1)配置 Forms 身份验证
Web.config 文件中的设置配置 Forms 身份验证。对于 Duwamish 7.0Web.Config 文件按如下所述指定 Forms 身份验证的使用:
    <authentication mode="Forms">
      <forms name=".ADUAUTH" loginUrl="secure/logon.aspx" protection="All">
      </forms>
    </authentication>
    <authorization>
      <allow users="*" />
    </authorization>
 
authentication元素只能在计算机、站点或应用程序级别声明。如果试图在配置文件中的子目录或页级别上进行声明,则将产生分析器错误信息。
 
如上所示,Web.Config .ADUAUTH 指定为身份验证 Cookie 的名称。当用户请求受限资源时,公共语言运行库将未经授权的用户重定向到在上面的 Web.Config 设置中指定的Login.aspxprotection="All" 设置指定应用程序使用数据验证和加密来保护 Cookie。若要进一步限制资源,Duwamish 7.0 会将安全资源放置到名为 secure 的子文件夹中并使用额外的 Web.Config 文件(在secure文件夹),在该文件中指定只有经过身份验证的用户才可访问该子文件夹的内容。
    <authorization>
      <deny users="?" />
      <allow users="*" />
    </authorization>
 
<deny users="?" /> 标记指定拒绝对所有匿名用户的访问。<allow users="*" /> 标签允许访问所有已验证身份的用户。
 
经过secure/logon.aspx认证通过的请求,重新定向最初的URL
// 将已验证身份的用户重定向回最初请求的 URL
FormsAuthentication.RedirectFromLoginPage("*", false);
 
2)用户定义的配置节
 <configSections>
    <section name="ApplicationConfiguration" type="Duwamish7.SystemFramework.ApplicationConfiguration, Duwamish7.SystemFramework" />
    <section name="DuwamishConfiguration" type="Duwamish7.Common.DuwamishConfiguration, Duwamish7.Common" />
    <section name="SourceViewer" type="System.Configuration.NameValueSectionHandler, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
 </configSections>
 
section元素包含配置节声明,name 指定配置节的名称,type 指定从配置文件中读取节的配置节处理程序类的名称。配置节处理程序(即实现 IConfigurationSectionHandler 接口的类)读取设置。
 
上面定义了3个配置节处理程序:Duwamish7.SystemFramework.ApplicationConfiguration
Duwamish7.Common.DuwamishConfiguration
System.Configuration.NameValueSectionHandler
 
前面两个配置节处理程序是由application提供的,后面的System.Configuration.NameValueSectionHandler.Net Framework提供的,该类也实现了IconfigurationSectionHandler接口,用来提供配置节中的名称/值对配置信息。
 
 
6web/PageBase.cs基类
最后谈谈web/PageBase.cs基类吧,所有 Duwamish 7.0 中的所有web页都继承名为 PageBase 的基类,该类实现 Duwamish 7.0 应用程序的 ASP.Net 页中使用的常见属性和方法,这种设计方法值得推荐。
 
Code Snippet分析:
/// <summary>
///     Handles errors that may be encountered when displaying this page.
///     <param name="e">An EventArgs that contains the event data.</param>
/// </summary>
protected override void OnError(EventArgs e)
{
    ApplicationLog.WriteError(ApplicationLog.FormatException(Server.GetLastError(), UNHANDLED_EXCEPTION));
    base.OnError(e);
}
重载OnError方法,使application遭遇到未处理错误的时候,自动调用ApplicationLog.WriteError()来记录错误日志。
 
另外,觉得Duwamishpassword处理有些特别,是以byte形式存放在Database中,避免明文的方式,以提高安全性(将在《Duwamish密码分析篇》进行中分析)。
 
 
Reference:
1, MSDN, Duwamish 7.0
Duwamish密码分析篇, Part 1
 
Written by: Rickie Lee
Nov. 05, 2004
 
继续前面关于DuwamishPOST,这里将学习Duwamish中关于Password的处理方式。Duwamish 7.0范例中的帐户密码通过SHA1散列运算和对散列执行Salt运算后,是以byte形式存放在Database中,避免明文的方式,以提高系统的安全性。
 
Duwamish的用户注册部分是封装在/web/modules/accountmodule.ascx用户控件内。随便提一下,Duwamish web tier中采用了大量的user control,并且所有的user control都继承/web/ModuleBase.cs 类,与web page继承PageBase.cs类相似,这种做法值得推荐。Duwamishuser control主要是封装一些相应的功能,模块化。这样不仅可以在本web项目内重用,而且以后维护也比较方便,如/web/modules/accountmodule.ascx user control就封装了用户注册部分的功能。
 
下面看看【用户注册】功能模块具体的实现代码(/web/modules/accountmodule.ascx):
1获取用户登记/注册password,并帐户密码执行散列运算。
byte [] bytePassword = null;
String tmpPassword = PasswordTextBox.Text;
 
if (tmpPassword == ConfirmPasswordTextBox.Text)
{
    SHA1 sha1 = SHA1.Create();
    bytePassword = sha1.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));
}
……
retVal = (new CustomerSystem()).CreateCustomer(EmailTextBox.Text,
                                           bytePassword,
                                           AcctNameTextBox.Text,
                                           AddressTextBox.Text,
                                           CountryTextBox.Text,
                                           PhoneTextBox.Text,
                                           FaxTextBox.Text,
                                           out moduleCustomerInfo);
 
先使用实现 160 SHA-1 标准的 System.Security.Cryptography 命名空间对密码进行散列运算。然后调用BusinessFacade/CustomerSystem类的CreateCustomer()方法。
 
知识点:
散列简介
散列(Hash)是一种单向算法,一旦数据被转换,将无法再获得其原始值。大多数开发人员使用数据库存储密码,如果密码直接以明文的形式存放在数据库中,则开发人员也能够看到这些密码,甚至包括用户的Credit Card信息。
不过,我们可以使用散列算法对密码进行加密,然后再将其存储在数据库中。用户输入密码后,可以再次使用散列算法对其进行转换,然后将其与存储在数据库中的散列进行比较。散列的特点之一是,即使原始数据只发生一个小小的改动,数据的散列也会发生非常大的变化。Rickie Ricky 这两个单词非常相似,但使用散列算法加密后的结果却相去甚远。你可能根本看不出二者之间有什么相似之处。
 
.NET 开发人员可以使用多种散列算法类。最常用的是 SHA1 MD5。下面我们看一下如何为Rickie这样的普通字符串生成散列,使任何人都无法识别它。
1)使用 SHA1 生成散列
通过如下的示例代码,来演示如何通过SHA1生成散列:
byte [] bytePassword = null;
string tmpPassword = txtPassword.Text.Trim();
 
// 创建新的加密服务提供程序对象
SHA1 sha1 = SHA1.Create();
// 将原始字符串转换成字节数组,然后计算散列,并返回一个字节数组
bytePassword = sha1.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));
// Releases all resources used by the System.Security.Cryptography.HashAlgorithm.
sha1.Clear();
// 返回散列值的 Base64 编码字符串
txtResults.Text = Convert.ToBase64String(bytePassword);
 
传递不同的字符串值来调用该例程,查看散列值的变化。例如,如果将字符串Rickie传递给该例程,输出结果:
v8ocXHBvlh4EqY/2HsJNH5XBVG0=
现在,将此过程中的输入值更改为Ricky。你将看到以下输出结果:
luQsSa61sB/7PT9piDx+OAGqCnI=
 
如此可见,输入字符串的一个小小变化就会产生完全不同的字符组合。这正是散列算法之所以有效的原因,它使我们很难找到输入字符串的规律,也很难根据加密后的字符弄清楚字符串原来的模样。
 
2)使用MD5也可以生成散列
通过如下的示例代码,来演示如何通过MD5生成散列:
byte [] bytePassword = null;
string tmpPassword = txtPassword.Text.Trim();
 
MD5 md5 = MD5.Create();
bytePassword = md5.ComputeHash(Encoding.Unicode.GetBytes(tmpPassword));
// Releases all resources used by the System.Security.Cryptography.HashAlgorithm.
md5.Clear();
txtResults.Text = Convert.ToBase64String(bytePassword);
 
输入RickieMD5散列算法的输出结果:
YUqR1JfNxrciyG0ixNj58A==
 
同样,加密后的字符串看起来也与原始输入相去甚远。这些散列算法对于创建没有任何意义的密码来说非常有用,也使黑客很难猜出这些密码。之所以使用散列算法,是因为可以用这种算法对密码进行加密并将其存储在数据库中。然后,当用户输入真实密码时,需要先对用户输入的密码进行同样的散列,然后通过网络发送到数据库中,比较它与数据库中的密码是否匹配。
 
请记住,散列是单向操作。使用散列算法对原始密码加密后将无法再恢复。
 
上述两种散列算法都执行同一种操作。不同之处只在于生成散列的密钥大小以及使用的算法。使用的密钥越大,加密就越安全。例如,MD5 使用的加密密钥比 SHA1 使用的密钥大,因此 MD5 散列较难破解。
 
对于散列算法要考虑的另外一点是,从实践或理论的角度上看是否存在冲突的可能性。冲突是我们所不希望的,因为两个不同的单词可能会生成相同的散列。例如,SHA1 从实践或理论上来讲没有发生冲突的可能性。MD5 从理论上讲有发生冲突的可能性,但从实践上讲没有发生冲突的可能性。因此,选择哪种算法归根结底取决于所需要的安全级别。
 
3Summary
一般情况下,将上述加密的字节数组,通过使用Convert.ToBase64String(bytePassword)方法把字节数组转换成 Base64 编码的字符串,然后存储在数据库中即可完成一般的商业应用。
 
 
2,调用BusinessFacade/CustomerSystem类,对散列执行Salt运算。
到目前为止,散列算法暴露出来的问题之一是,如果两个用户碰巧使用相同的密码,那么散列值将完全相同。如果黑客看到您存储密码的表格,会从中找到规律并明白您很可能使用了常见的词语,然后黑客会开始词典攻击以确定这些密码。要确保任何两个用户密码的散列值都不相同,一种方法是在加密密码之前,在每个用户的密码中添加一个唯一的值。这个唯一值称为“盐”值(Salt)。
 
虽然对密码执行散列运算是一个好的开端,但若要增加免受潜在攻击的安全性,则可以对密码散列执行 Salt 运算。Salt 就是在已执行散列运算的密码中插入的一个随机数字。这一策略有助于阻止潜在的攻击者利用预先计算的字典攻击。字典攻击是攻击者使用密钥的所有可能组合来破解密码的攻击。当您使用 Salt 值使散列运算进一步随机化后,攻击者将需要为每个 Salt 值创建一个字典,这将使攻击变得非常复杂且成本极高。
 
Salt 值随散列存储在一起,并且未经过加密。所存储的 Salt 值可以在随后用于密码验证。
 
下面看看Duwamish 7.0中是如何实现Salt运算:
1BusinessFacade/CustomerSystem classCreate Customer()方法
public bool CreateCustomer(String emailAddress,
                           byte [] password,
                           String name,
                           String address,
                           String country,
                           String phoneNumber,
                           String fax,
                           out CustomerData custData)
{
    // create a salted password
    byte [] saltedPassword = CreateDbPassword(password);
 
    //
    // Create a new row
    //
    custData = new CustomerData();
   
    DataTable table = custData.Tables[CustomerData.CUSTOMERS_TABLE];
    DataRow row = table.NewRow();
    //
    // Fill input data into new row
    //
    row[CustomerData.EMAIL_FIELD] = emailAddress;
    row[CustomerData.PASSWORD_FIELD] = saltedPassword;
    row[CustomerData.NAME_FIELD] = name;
    row[CustomerData.ADDRESS_FIELD] = address;
    row[CustomerData.COUNTRY_FIELD] = country;
    row[CustomerData.PHONE_FIELD] = phoneNumber;
    row[CustomerData.FAX_FIELD] = fax;
    //
    // Add it to the table
    //
    table.Rows.Add(row);
    // 调用Business rules tierCustomer Class
    // Insert the customer using the business rules
    //
    return (new Customer()).Insert(custData);
}
首先调用Facade/CustomerSystem 类的私有方法CreateDbPassword(),获取对散列执行Salt运算结果(长度为24个字节的byte数组),然后调用Business rules tier中的Customer classInsert()方法,将用户信息,包括密码存放在数据库中。
 
2Facade/CustomerSystem 类的私有方法 CreateDbPassword()
// create salted password to save in Db
private byte [] CreateDbPassword(byte[] unsaltedPassword)
{
          //Create a salt value
          byte[] saltValue = new byte[saltLength];
          RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider();
          //用加密型强随机字节填充的数组
          rng.GetBytes(saltValue);
         
          return CreateSaltedPassword(saltValue, unsaltedPassword);
}
上述代码片断使用 .NET Framework RNGCryptoServiceProvider 创建一个随机的数字字符串。RNG 表示随机数生成器。该类可以创建一个任意长度的随机字节数组,长度由您指定。您可以使用此随机字节数组作为散列算法的Salt值。要采用这种方法,必须安全地存储该Salt值。
 
saltLength=4(常量),Duwamish 7 示例用RNGCryptoServiceProvider创建一个 4 字节 Salt 值。然后调用Facade/CustomerSystem 类的私有方法CreateSaltedPassword(),获取对散列执行Salt运算后的结果。
 
3Facade/CustomerSystem 类的私有方法CreateSaltedPassword()
// create a salted password given the salt value
private byte[] CreateSaltedPassword(byte[] saltValue, byte[] unsaltedPassword)
{
          // add the salt to the hash
          byte[] rawSalted = new byte[unsaltedPassword.Length + saltValue.Length];
         
          // Copies all the elements of the current one-dimensional System.Array to the specified one-dimensional System.Array starting at the specified destination System.Array index.
         
          unsaltedPassword.CopyTo(rawSalted,0);
          saltValue.CopyTo(rawSalted,unsaltedPassword.Length);
         
          //Create the salted hash                      
          SHA1 sha1 = SHA1.Create();
          byte[] saltedPassword = sha1.ComputeHash(rawSalted);
 
          // add the salt value to the salted hash
          byte[] dbPassword = new byte[saltedPassword.Length + saltValue.Length];
          saltedPassword.CopyTo(dbPassword,0);
          saltValue.CopyTo(dbPassword,saltedPassword.Length);
 
          return dbPassword;
}
 
该方法根据传入的Salt值(长度为4个字节的byte数组)和已执行散列运算的密码(长度为20个字节的byte数组),拼接为长度为24byte数组。然后对上述拼接后的数组再进行SHA1散列运算,得到结果saltedPassword(长度为20个字节的byte数组)。
 
最后将saltedPassword(长度为20个字节的byte数组)和Salt值(长度为4个字节的byte数组)拼接为dbPassword(长度为4个字节的byte数组)返回。
 
3,调用BusinessRules/Customer类的Insert()方法。
Insert()方法根据传入的CustomerData对象,验证数据的合法性,然后调用Data Access tierCustomers对象的InsertCustomer()方法。
具体代码请参考Duwamish 7.0范例。
 
4,调用DataAccess/Customers类的InsertCustomer()方法。
InsertCustomer()方法根据传入的CustomerData对象,调用Database端的Stored Procedure,执行真正的数据库insert操作。可以观察到Duwamish7 DatabaseCustomers表的Password字段类型为binary且长度为24
具体代码请参考Duwamish 7.0范例。
 
下一篇POSTDuwamish密码分析篇 Part 2将分析【用户登录】流程的密码验证过程。
 
 
References:
1, MSDN, Duwamish 7.0
2, Paul D. Sheriff, Microsoft .NET 中的简化加密, http://www.microsoft.com/china/MSDN/library/archives/library/dnnetsec/html/cryptosimplified.asp
Duwamish密码分析篇, Part 2
 
 
继续前面关于DuwamishPOST,这里将学习Duwamish中关于Password的处理方式。Duwamish 7.0范例中的帐户密码通过SHA1散列运算和对散列执行Salt运算后,是以byte形式存放在Database中,避免明文的方式,以提高系统的安全性。
 
1,【用户登录】过程概述
Web 层中启动登录过程。用户输入电子邮件地址和密码(凭据),然后单击“Logon”(登录)按钮,这将调用 Duwamish7.Web.Logon.LogonButton_Click 方法。下一步,Duwamish7.Web.Logon.LogonButton_Click 方法创建密码的散列表示形式,并将凭据传递给业务外观层的 Duwamish7.BusinessFacade.CustomerSystem.GetCustomerByEmail 方法。接着 Duwamish7.DataAccess.Customers.LoadCustomerByEmail 方法调用数据访问层,后者又调用 GetCustomerByEmail 存储过程 (SPROC)。然后通过 ComparePasswords 方法,相对于从数据库中检索的经过 salt 和散列运算的密码来验证散列密码。如果凭据有效,则客户帐户信息成功地存储到 Cart 对象,并且 ASP.NET Forms 身份验证通过 pageBase ShoppingCart.Customer() 属性验证凭据。如果凭据无效,则 MismatchLabel 设置为可见,它在 ASP.NET 页上显示下面的内容:“Invalid email address or password- please try again”(电子邮件地址或密码无效,请再试一次)。
 
2,下面看看【用户登录】验证功能模块具体的实现代码
1Duwamish7.Web.Logon.LogonButton_Click 方法
该方法首先创建用户输入密码的SHA1散列形式,然后调用业务外观层的 Duwamish7.BusinessFacade.CustomerSystem.GetCustomerByEmail 方法。
    //
    // Check the Email and Password combination
    //
    SHA1 sha1 = SHA1.Create();
    byte [] password = sha1.ComputeHash(Encoding.Unicode.GetBytes(LogonPasswordTextBox.Text));
 
    custData = (new CustomerSystem()).GetCustomerByEmail(LogonEmailTextBox.Text, password);
   
    if (custData != null)   //were they valid?
    {
        //
        // 1. Update customer in session.
        // 2. Update customer in cart.
        //
        base.Customer = custData;
        base.ShoppingCart().Customer = custData;
        // 将已验证身份的用户重定向回最初请求的 URL
        FormsAuthentication.RedirectFromLoginPage("*", false);
    }
    else
    {
        MismatchLabel.Visible = true;
    }
 
如果凭据有效,则客户帐户信息成功地存储到 Cart 对象,并且 ASP.NET Forms 身份验证通过 pageBase ShoppingCart.Customer() 属性验证凭据。如果凭据无效,则 MismatchLabel 设置为可见,它在 ASP.NET 页上显示下面的内容:“Invalid email address or password- please try again”(电子邮件地址或密码无效,请再试一次)
 
2)业务外观层的 CustomerSystem.GetCustomerByEmail 方法
根据用户的email,获取DatabaseCustomerPassword,该Password已执行散列运算和对散列执行过Salt运算,是24个字节长度的byte数组。
public CustomerData GetCustomerByEmail(String emailAddress, byte [] password)
{
      //
      // Check preconditions
      //
      ApplicationAssert.CheckCondition(emailAddress != String.Empty, "Email address is required", ApplicationAssert.LineNumber);
      ApplicationAssert.CheckCondition(password.Length != 0, "Password is required", ApplicationAssert.LineNumber);
      //
      // Get the customer dataSet
      //
      CustomerData dataSet;
      using (DataAccess.Customers customersDataAccess = new DataAccess.Customers())
      {
           dataSet = customersDataAccess.LoadCustomerByEmail(emailAddress);
      }
      //   
      // Verify the customer's password
      //
      DataRowCollection rows = dataSet.Tables[CustomerData.CUSTOMERS_TABLE].Rows;
 
      if ( ( rows.Count == 1 ))
      {
           byte [] dbPassword = (byte[])rows[0][CustomerData.PASSWORD_FIELD];
 
           if (ComparePasswords (dbPassword, password))
                 return dataSet;
           else
                 return null;
      }
      else
           return null;
}
 
在获取到DataAccess层返回的CustomerData对象后,进一步调用类中的私有方法ComparePasswords()。该方法负责从Password字段值中提取Salt值,然后运用该Salt值对传入的SHA1散列执行Salt运算。
 
其中CreateSaltedPassword()方法和前面【用户注册】过程相同,用来对散列结果再次执行Salt运算。
// compare the hashed password against the stored password
private bool ComparePasswords(byte[] storedPassword, byte[] hashedPassword)
{
      if (storedPassword == null || hashedPassword == null || hashedPassword.Length != storedPassword.Length - saltLength)
           return false;
 
      // get the saved saltValue
      // 获取已保存在password数据字段中的Salt
      byte[] saltValue = new byte[saltLength];
      int saltOffset = storedPassword.Length - saltLength;
      for (int i = 0; i < saltLength; i++)
           saltValue[i] = storedPassword[saltOffset + i];
 
      byte[] saltedPassword = CreateSaltedPassword(saltValue, hashedPassword);
 
      // compare the values
      return CompareByteArray(storedPassword, saltedPassword);
}
 
按字节比较两个字节数组是否相等,分别传入数据表中Password字段byte数组和对当前散列执行Salt运算后的byte数组。
// compare the contents of two byte arrays
private bool CompareByteArray(byte[] array1, byte[] array2)
{
      if (array1.Length != array2.Length)
           return false;
      for (int i = 0; i < array1.Length; i++)
      {
           if (array1[i] != array2[i])
                 return false;
      }
      return true;
}
 
3)数据访问层的Customers.LoadCustomerByEmail()方法
该方法根据传入的emailAddress参数,查询Database,返回CustomerData对象。过程比较简单,详细代码请查询Duwamish 7.0范例。
 
3Summary
通过对Duwamish 7.0范例中用户注册和用户登录验证过程的分析,我们确信用户Password以安全的Salt运算结果存放在后台的Database中。
其实,在实际的应用系统中,上述的加密过程有一个小问题:就是当有大量的用户忘记了自己的Password,如何帮助他们恢复自己的Password呢?这个问题地球人都不知道,只有通过另外的application来将这些Password重新Update为新的Password,因为散列是单向操作,使用散列算法对原始密码加密后将无法再恢复。
 
因此,将在Duwamish密码分析篇, Part 3中分析如何实现双向的加密/解密操作,来克服上面提出的问题。
 
References:
1, MSDN, Duwamish 7.0
Duwamish密码分析篇, Part 3
 
Written by: Rickie Lee
Nov. 07, 2004
 
通过前面关于《Duwamish密码分析篇, Part 12》的POST,可以了解到Duwamish中关于Password的处理方式。Duwamish 7.0范例中的帐户密码通过SHA1散列运算和对散列执行Salt运算后,然后以byte形式存放在Database中,避免明文的方式,以提高系统的安全性。
 
但是,由于散列是单向操作,使用散列算法对原始密码加密后将无法再恢复。因此,在实际的应用系统中,上述的加密过程有一个小问题:就是当有大量的用户忘记了自己的Password,如何帮助他们恢复自己的Password呢?这个问题地球人都不知道。
 
因此,对于一般的商业应用,通过.Net的加密/解密类库来同时实现上述目的,既加密Password等重要信息,同时也可以在必要的时候解密这些信息。
 
加密算法使可以将数据掩盖起来,除了特定人员能够对其解密外,其他人员不大可能通过数学方法读取该数据。但如果希望读取该数据,则可以为其提供一个特定的“密钥”,使其能够解密并读取数据。.NET Framework 中有多种可用的加密/解密算法。
 
目前有两种加密方法:
对称算法(或密钥算法)的速度非常快,非常适于加密大型的数据流。这些算法可以加密数据,也可以解密数据。对称加密技术的数据交换两边(即加密方和解密方)必须使用一个保密的私有密钥。
不对称算法(或公钥算法)没有对称算法快,但其代码较难破密。这些算法取决于两个密钥,一个是私钥,另一个是公钥。公钥用来加密消息,私钥是可以解密该消息的唯一密钥。公钥和私钥通过数学方法链接在一起,因此要成功进行加密交换,必须获得这两个密钥。由于可能会影响到计算机性能,因此不对称算法不太适用于加密大量数据。不对称算法的常见用法是将对称密钥和初始化向量加密并传输给对方。然后在双方之间来回发送的消息中使用对称算法加密和解密数据。不对称算法主要有RSADSA等,主要用于网络数据的加密。保护HTTP传输安全的SSL就是使用非对称技术。
 
本文先主要学习.Net中如下对称算法(或密钥算法)类库,包括以下几种:
DESData Encryption Standard),TripleDESRC2Rijndael
对称算法(或密钥算法)使用一个密钥和一个初始化向量 (Initialization VectorIV) 来保证数据的安全。加密的功效取决于所用密钥的大小,密钥越长,保密性越强。典型的密钥长度有64位、128位、192位、256位和512位。使用该数据的双方都必须知道这个密钥和初始化向量才能够加密和解密数据。必须确保该密钥的安全,否则其他人将有可能解密该数据并读取该消息。初始化向量只是一个随机生成的字符集,使用它可以确保任何两个文本都不会生成相同的加密数据。然后,在此基础上学习开发一套标准的加密/解密通用类库,供今后开发应用系统时使用。
 
.Net Framework内置加密/解密算法类库
.NET Framework 为各种最广泛使用的对称加密算法提供了支持。.NET构架从基本的SymmetricAlgorithm类扩展出来四种算法:
·System.Security.Cryptography.DES
·System.Security.Cryptography.TripleDES
·System.Security.Cryptography.RC2
·System.Security.Cryptography.Rijndael
.NET的对称加密和解密通过CryptoStream类来处理的,它继承自System.IO.Stream,使用 CryptoStream 对象的 Write 方法将数据写入到内存数据流(Memory Stream),这就是进行实际加密/解密的方法。
 
先以DES算法为例,了解.Net为我们提供的简单加密/解密过程:
1DES加密
上图Encrypt实例代码:
private void btnEncrypt_Click(object sender, System.EventArgs e)
{
          SymmetricAlgorithm mCSP = new DESCryptoServiceProvider();
          ICryptoTransform ct;
          MemoryStream ms;
          CryptoStream cs;
          byte[] byt;
          byte [] key, iv;
 
          // If this property is a null reference (Nothing in Visual Basic) when it is used,
          // GenerateKey is called to create a new random value.
          key = mCSP.Key;
          // If this property is a null reference (Nothing in Visual Basic) when it is used,
          // GenerateIV is called to create a new random value.
          iv = mCSP.IV;
          // Creates a symmetric Data Encryption Standard (DES) encryptor object.
          ct = mCSP.CreateEncryptor(key, iv);
          // Refresh textboxes
          txtKey.Text = Convert.ToBase64String(key);
          txtIV.Text = Convert.ToBase64String(iv);
 
          byt = Encoding.Unicode.GetBytes(txtOriginal.Text.Trim());
 
          ms = new MemoryStream();
          cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
          cs.Write(byt, 0, byt.Length);
          cs.FlushFinalBlock();
 
          cs.Close();
 
          txtResults.Text = Convert.ToBase64String(ms.ToArray());
}
首先创建 DESCryptoServiceProvider 对象,要使用对称算法,必须提供要使用的密钥。每个 CryptoSymmetricAlgorithm 实现都提供一种 GenerateKey 方法。它们实际上使用的是公共语言运行时 (CLR) 类中内置的随机数生成器类。在首次调用DESCryptoServiceProvider 对象的Key属性时,因为该属性为Null,则自动调用GenerateKey 方法来生成随机Key。密钥的大小取决于用来加密的特定提供程序。例如,DES 密钥的大小为 64 位,而 TripleDES 密钥的大小为 192 位。每个 SymmetricAlgorithm 类上都有一个 KeySize 属性,它将返回用于生成密钥的密钥大小。
然后是需要生成初始化向量 (IV),如果IV属性为Null,也是自动调用GenerateIV方法,生成IV。只要使用的 IV 不同,即使密钥相同,同一个数据也会被加密成完全不同的值。当然,你可以自己指定特定的KeyIV,但要注意的不同的加密算法,可能采用不同的Key长度和IV长度。
ICryptoTransform 是一个接口。需要此接口才能在任何服务提供程序上调用 CreateEncryptor 方法,服务提供程序将返回定义该接口的实际 encryptor 对象。
Encoding.Unicode.GetBytes()方法负责将原始字符串转换成字节数组。大多数 .NET 加密算法处理的是字节数组而不是字符串。
现在可以执行实际的加密了。此进程需要创建一个数据流,用于将加密的字节写入到其中。要使用名为 ms MemoryStream 对象、ICryptoTransform 对象(提供给 CryptoStream 类的构造函数)以及说明您希望在何种模式(读、写等)下创建该类的枚举常数。创建 CryptoStream 对象 cs 后,现在使用 CryptoStream 对象的 Write 方法将数据写入到内存数据流。这就是进行实际加密的方法,加密每个数据块时,数据将被写入 MemoryStream 对象。
创建 MemoryStream 后,该代码将在 CryptoStream 对象上执行 FlushFinalBlock 方法,以确保所有数据均被写入 MemoryStream 对象。并调用CryptoStream 对象的Close方法关闭 CryptoStream 对象。
最后,调用 Convert.ToBase64String() 方法,该方法接受字节数组输入并使用 Base64 编码方法将加密结果编码为可读内容,输出到TextBox显示。
 
2DES解密
上图Decrypt实例代码:
private void btnDecrypt_Click(object sender, System.EventArgs e)
{
          SymmetricAlgorithm mCSP = new DESCryptoServiceProvider();
          ICryptoTransform ct;
          MemoryStream ms;
          CryptoStream cs;
          byte[] byt;
          // Sets the key for the symmetric algorithm.
          mCSP.Key = Convert.FromBase64String(txtKey.Text.Trim());
          // Sets the initialization vector (IV) for the symmetric algorithm.
          mCSP.IV = Convert.FromBase64String(txtIV.Text.Trim());
          // Creates a symmetric Data Encryption Standard (DES) decryptor object.
          ct = mCSP.CreateDecryptor(mCSP.Key, mCSP.IV);
 
          byt = Convert.FromBase64String(txtOriginal.Text.Trim());
 
          ms = new MemoryStream();
          cs = new CryptoStream(ms, ct, CryptoStreamMode.Write);
          cs.Write(byt, 0, byt.Length);
          cs.FlushFinalBlock();
 
          cs.Close();
 
          txtResults.Text = Encoding.Unicode.GetString(ms.ToArray());
 
}
 
Decrypt过程代码与Encrypt过程很相似,只有细微的差别。首先,Decrypt过程不需要重新生成随机的KeyIV,必须使用与上述Encrypt过程中相同的KeyIV,否则无法解密。
另外,ICryptoTransform 是一个接口,这里是在DESCryptoServiceProvider服务提供程序上调用CreateDecryptor方法,服务提供程序将返回定义该接口的实际decryptor对象。
 
.Net Framework也支持其他加密/解密算法,如TripleDESRC2Rijndael,并且只要简单替换上述代码的DESCryptoServiceProvider服务提供程序对象,就可以切换到对应的加密算法。正是基于这一点,我们可以非常方便地构建一套标准的加密/解密通用类库,请参考Microsoft的《如何创建加密库》,这里不重复了。
根据Microsoft的《如何创建加密库》编写的一个DEMO
知识点:Base64编码
前面的加密/解密过程中,使用到.Net Framework提供的关于Base64编码的方法:
1Convert.ToBase64String() 方法,该方法接受字节数组输入并使用 Base64 编码方法将加密结果编码为可读内容
2Convert.FromBase64String()方法,该方法接受Base64 编码的字符串,输出字节数组。
 
Base64MIME邮件中常用的编码方式之一。它的主要思想是将输入的字符串或数据编码成只含有{'A'-'Z', 'a'-'z', '0'-'9', '+', '/'}64个可打印字符的串,故称为“Base64”。
Base64编码的方法是,将输入数据流每次取6 bit,不足6bit的补0,用此6 bit的值(0-63)作为索引去查表,输出相应字符。这样,每3个字节将编码为4个字符(3×8 4×6)(之后在6位的前面补两个0,形成8位一个字节的形式),不满4个字符的以'='填充。转换后的字符串理论上将要比原来的长1/3
 
现在回到前面的DES算法中Key或者IVKey长度为64位(8个字节),为了让Base64编码是4的倍数,就要补1个等号。
 
 
References:
1, MSDN, Duwamish 7.0
Duwamish Web Services分析篇
 
Written by: Rickie Lee
Nov. 08, 2004
 
Duwamish 7.0web项目中提供了一个Web Serviceservice/catalogservice.asmx),以向 Internet 公开它的书目录搜索功能。CatalogService Web Service由一个asmx文件和一个代码隐藏文件组成,其中ASMX 文件充当调用 Web Services的客户端的基 URL,代码隐藏文件包含实现 Web 服务的代码。不过,在整个Duwamish项目中并没有调用该web service,正如以前的POST中所提及的:
If you need to communicate between applications (even .NET apps) then use web services. Note this is not between tiers, but between applications – as in SOA (Service-Oriented Architecture). SOA is not useful INSIDE applications. It is only useful BETWEEN applications.
 
1. Web Services概述
Web Services既可以在内部由单个应用程序使用,也可通过 Internet 公开以供外部的应用程序使用。由于可以通过标准接口访问,因此Web Services使异类系统能够作为单个计算网络资源协同运行。
Web Services并不追求一般的代码可移植性功能,而是为实现数据和系统的互操作性提供了一种可行的解决方案。Web Services使用基于XML的消息处理作为基本的数据通讯方式,以帮助消除使用不同组件模型、操作系统和编程语言的系统之间存在的差异。开发人员可以用像过去在创建分布式应用程序时使用组件一样的方式创建将来自各种平台的Web Services组合在一起的应用程序。
Web Services的核心特征之一是服务的实现与使用之间的高度抽象化。通过将基于XML的消息处理机制,Web Services客户端和Web Services提供程序之间除输入、输出和位置之外无需互相了解其他信息。
Web Services向外界发布出一个能够通过Web进行调用的、平台无关的API。也就是说,你能够在任何你喜欢的平台上,用编程的方法通过Web来调用这个应用程序,进行基于Web的分布式计算和处理。Web Services平台是一套标准,它定义了应用程序如何在Web上实现互操作性。Web Services平台采用XML来表示数据的基本格式,采用W3C制定的XML Schema(XSD)来作为其数据类型系统。
组成Web Services平台的三个核心的技术规范分别为SOAPWSDLUDDISOAP规范定义了SOAP消息的格式,以及怎样通过HTTP协议来使用SOAP,来执行Web Services的调用。WSDLWeb Services描述语言)用来描述Web Services。因为其基于XML,所以WSDL文档既是机器可阅读的,又是人可阅读的。UDDI(统一描述,发现和集成协议)标准定义了Web Services的发布与发现的方法。
从技术的角度来看,Web Services可以被认为是一种部署在Web上的对象(Web Object),因此,具有对象技术所承诺的所有优点;同时,Web Services的基石是以XML为主的、开放的Web规范技术,因此,具有比任何现有对象技术更好的开放性。
 
2. Duwamish中的CatalogService Web Service
(1)CatalogServer.asmx文件中仅包含一行代码:
<%@ WebService Language="c#" Codebehind="CatalogService.cs" Class="Duwamish7.Web.Service.CatalogService" %>
 
2CatalogService.cs代码隐藏文件包含实现web service的代码:
CatalogService Web 服务实现 GetBooksByTopic GetBooksByTopicSecure Web 方法,返回值为DataSet类型(支持XML编码和序列化)。Web Service发布的上述Web方法均都有WebMethodAttribute
 
WebMethodAttribute向使用 ASP.NET 创建的 XML Web services 中的某个方法添加此特性后,就可以从远程 Web 客户端调用该方法。
 
另外还有一些辅助的class和方法(调用BusinessFacade tier),代码比较简单。
 
3Web.config配置文件<webServices> 元素:可以配置使用 ASP.NET 创建的 XML Web services 的设置。
 
 
3. Summary
Web Services不仅可用于异构平台的相互集成,也是分布式应用开发的一种技术。Microsoft在推.Net Framework时,尽心尽力吹捧这项技术,并冠以XML Web Services。不过由于Web Services的性能不好的问题,感觉目前在企业内部应用并不多,估计还不及.Net Remoting技术的应用。
Microsoft还有一个Web Services的增强软件开发包:Web Services Enhancements (WSE) Version 2.0,主要提供如下特性:安全特性(数字签名和加密),消息路由,消息附件等等,从Reference 1可以下载。
现在,Web Services方面的相关规范很多,如WS-Security, WS-Policy, WS-Trust, WS-SecureConversation……,令人目不暇接,在不断地向前发展。从另外一个方面也表示,Web Services技术目前在企业应用方面还不够成熟。
 
 
References:
2, MSDN, Duwamish 7.0

相关文章推荐

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

安装Duwamish7.0报错解决方法

朋友推荐微软的Duwamish7.0和Pet shop是两个不错的学习架构设计的经典案例。可在安装Duwamish7.0时报错。错误提示信息为:The   installer   was   inte...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

MSMQ消息队列与WCF MSMQ的认识与使用

先说一下MSMQ MSMQ全称MicroSoft Message Queue,微软消息队列,是在多个不同的应用之间实现相互通信的一种异步传输模式,相互通信的应用可以分布于同一台机器上,也可以分布...

Delphi7高级应用开发随书源码

  • 2003年04月30日 00:00
  • 676KB
  • 下载

MSMQ-发送消息到远程专用队列path格式

在工作组模式下,远程访问专用队列。在网上找到一篇文章,翻译了一下。 最后结论,直接使用多元素格式名方式,利用IP地址直接对单个或多个目标发送消息      MessageQueue rmQ...

Duwamish 7.0 系列分析文章

转自:http://blog.csdn.net/cngkqy/article/details/745229 Duwamish架构分析篇 Duwamish是Microsoft提供一个企业级的...

Duwamish 7.0(C# 版和 VB.NET 版)

  • 2004年06月16日 00:00
  • 5.9MB
  • 下载
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:Duwamish 7.0 系列分析文章
举报原因:
原因补充:

(最多只允许输入30个字)