<?xml version="1.0" encoding="utf-8"?><feed xmlns="http://www.w3.org/2005/Atom"><title type="text">博客园_不如来编码-luminji's web</title><subtitle type="text">*慢，始终细嚼慢咽；*应无所住而生其心；*思惑：贪、嗔、痴、慢、疑；*离头三尺有神灵；*优秀源自于将良好的行为演变成习惯；</subtitle><id>http://feed.cnblogs.com/blog/u/75340/rss</id><updated>2012-06-01T08:54:51Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><generator>feed.cnblogs.com</generator><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/"/><link rel="self" type="application/atom+xml" href="http://feed.cnblogs.com/blog/u/75340/rss"/><entry><id>http://www.cnblogs.com/luminji/archive/2012/05/30/2511357.html</id><title type="text">Asp.net安全架构之2：Session hijacking（会话劫持）</title><summary type="text">原理会话劫持是指通过非常规手段，来得到合法用户在客户端和服务器段进行交互的特征值（一般为sessionid），然后伪造请求，去访问授权用户的数据。获取特征值的非常规有段主要有如下几种：首先是猜测的方式，如果我们的sessionid的生成是有规律的，那么使用猜测的方式就可以到达非法获取的目的，如图所示：其次是session fixation攻击。session fixation攻击是指用户通过XSS、网络嗅探、本地木马来得到特征值，这些交互的特征值一般来说放置在浏览器的Cookie中（当然，我们也知道sessionid也可以通过URL来传递，这样的话，获取就简单多了）。然后诱使用户去完成一次登录</summary><published>2012-05-30T00:34:00Z</published><updated>2012-05-30T00:34:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2012/05/30/2511357.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2012/05/30/2511357.html"/><content type="html">&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;会话劫持是指通过非常规手段，来得到合法用户在客户端和服务器段进行交互的特征值（一般为sessionid），然后伪造请求，去访问授权用户的数据。&lt;/p&gt;&lt;p&gt;获取特征值的非常规有段主要有如下几种：&lt;/p&gt;&lt;p&gt;首先是猜测的方式，如果我们的sessionid的生成是有规律的，那么使用猜测的方式就可以到达非法获取的目的，如图所示：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/123061/2012051720490054.png" alt="" width="678" height="526" /&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;其次是session fixation攻击。session fixation攻击是指用户通过XSS、网络嗅探、本地木马来得到特征值，这些交互的特征值一般来说放置在浏览器的Cookie中（当然，我们也知道sessionid也可以通过URL来传递，这样的话，获取就简单多了）。然后诱使用户去完成一次登录（诱使的方法可使发邮件，发链接等）。如果服务器没有更新这个SessionID，则攻击者可以凭借此SessionID登录系统，如图所示：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/123061/2012051720492416.png" alt="" width="675" height="605" /&gt;&lt;/p&gt;&lt;p&gt;在特征值被获取到之后，攻击者还可以使用Session保持攻击，这一般是指写一段小代码，定时发送请求，保持Session有效。这样，攻击者，就可以一直利用这个合法用户进行非法活动。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;实际案例&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在&lt;a href="http://192.168.40.193/portal/default.aspx"&gt;http://yourdomain.com/default.aspx&lt;/a&gt;界面，我们偷到了sessionID，然后就可以模拟这样的请求来完成一次攻击。一次攻击是指，拿上如下的请求，我们可以在任意客户端进行登录。&lt;/p&gt;&lt;p align="left"&gt;User-Agent: Fiddler&lt;/p&gt;&lt;p align="left"&gt;Host: 192.168.40.193&lt;/p&gt;&lt;p&gt;Cookie: LoginName=luminji; ASP.NET_SessionId=xzyplp45wgl3rf45ssxt5h55;&lt;/p&gt;&lt;p&gt;同时，在客户端写一段脚本，还可以完成Session保持攻击。经过这样的处理后，非法用户不用登录系统就可以访问任意页面了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;应对策略&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;1：为Cookie设置httpOnly，就可以有效防止Cookie被非法读取，从而防止劫持；&lt;/p&gt;&lt;p&gt;2：每次登录更换SessionID。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;具体措施&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查看sessionid生成策略，确保不可被猜测&lt;/p&gt;&lt;p&gt;备注，sessionid在asp.net程序中是被自动生成为GUID形式的，所以在Asp.net程序中该项不需要被修改。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查看sessionid保存策略，确保不通过URL进行传递&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 每次登录更换sessionid&lt;/p&gt;&lt;p&gt;改进登录模块，每次登录更换sessionid。更换sessionid并不会影响有些站点的类似&amp;ldquo;一个星期&amp;rdquo;都不用登录的功能。如果攻击者不是从合法用户的本机获取的sessionid，那么完成此次升级后，session fixation攻击就被阻断了。&lt;/p&gt;&lt;p&gt;更换SessionID的函数为：&lt;/p&gt;&lt;div style="background-color: #F5F5F5;border: 1px solid #CCCCCC;padding:10px;"&gt;        &lt;span style="color: #0000ff;"&gt;private&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;void&lt;/span&gt;&lt;span style="color: #000000;"&gt; ChangeSessionID()&lt;br/&gt;        {&lt;br/&gt;            SessionIDManager m &lt;/span&gt;= &lt;span style="color: #0000ff;"&gt;new&lt;/span&gt;&lt;span style="color: #000000;"&gt; SessionIDManager();&lt;br/&gt;            m.RemoveSessionID(Context);&lt;br/&gt;            HttpApplication ctx &lt;/span&gt;=&lt;span style="color: #000000;"&gt; (HttpApplication)Context.ApplicationInstance;&lt;br/&gt;            HttpModuleCollection mods &lt;/span&gt;=&lt;span style="color: #000000;"&gt; ctx.Modules;&lt;br/&gt;            SessionStateModule sessionStateModule &lt;/span&gt;= (SessionStateModule)mods.Get(&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;Session&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br/&gt;            System.Reflection.MethodInfo CreateSessionId &lt;/span&gt;=&lt;span style="color: #000000;"&gt; &lt;br/&gt;                sessionStateModule.GetType().GetMethod(&lt;br/&gt;                    &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;CreateSessionId&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;br/&gt;                    BindingFlags.Instance &lt;/span&gt;|&lt;span style="color: #000000;"&gt; BindingFlags.NonPublic);&lt;br/&gt;            System.Reflection.MethodInfo InitStateStoreItem &lt;/span&gt;=&lt;span style="color: #000000;"&gt; &lt;br/&gt;                sessionStateModule.GetType().GetMethod(&lt;br/&gt;                    &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;InitStateStoreItem&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;, &lt;br/&gt;                    BindingFlags.Instance &lt;/span&gt;|&lt;span style="color: #000000;"&gt; BindingFlags.NonPublic);&lt;br/&gt;            CreateSessionId.Invoke(sessionStateModule, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;null&lt;/span&gt;&lt;span style="color: #000000;"&gt;);&lt;br/&gt;            SessionStateUtility.RemoveHttpSessionStateFromContext(Context);&lt;br/&gt;            InitStateStoreItem.Invoke(sessionStateModule, &lt;/span&gt;&lt;span style="color: #0000ff;"&gt;new&lt;/span&gt; &lt;span style="color: #0000ff;"&gt;object&lt;/span&gt;[] { &lt;span style="color: #0000ff;"&gt;true&lt;/span&gt;&lt;span style="color: #000000;"&gt; });&lt;br/&gt;&lt;br/&gt;            FieldInfo sessioninfo &lt;/span&gt;=&lt;br/&gt;                    &lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;.GetType().BaseType.BaseType.GetField(&lt;br/&gt;                        &lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #800000;"&gt;_session&lt;/span&gt;&lt;span style="color: #800000;"&gt;"&lt;/span&gt;&lt;span style="color: #000000;"&gt;,&lt;br/&gt;                        BindingFlags.NonPublic &lt;/span&gt;|&lt;span style="color: #000000;"&gt; BindingFlags.Instance );&lt;br/&gt;            sessioninfo.SetValue(&lt;/span&gt;&lt;span style="color: #0000ff;"&gt;this&lt;/span&gt;&lt;span style="color: #000000;"&gt;, HttpContext.Current.Session);&lt;br/&gt;        }&lt;/span&gt;&lt;/div&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 确保登录逻辑不仅仅依赖sessionid&lt;/p&gt;&lt;p&gt;如果登录逻辑不仅仅依赖sessionid，攻击者将需要得到全部的特征值（一般情况下，就是说意味着他要得到全部和登录相关的cookie值），这加大了攻击难度。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 确保Cookie的httponly&lt;/p&gt;&lt;p&gt;通过文件查找所有的&amp;rdquo;cookie&amp;rdquo;，找出所有设置cookie的地方，为用于认证的cookie设置如下的格式：&lt;/p&gt;&lt;p&gt;Set-Cookie: cookieName=cookieValue;httponly&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2511357.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2012/05/30/2511357.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2012/05/22/2507185.html</id><title type="text">Asp.net安全架构之1：xss（跨站脚本）</title><summary type="text">原理跨站脚本（Cross site script，简称xss）是一种“HTML注入”，由于攻击的脚本多数时候是跨域的，所以称之为“跨域脚本”。我们常常听到“注入”（Injection），如SQL注入，那么到底“注入”是什么？注入本质上就是把输入的数据变成可执行的程序语句。SQL注入是如此，XSS也如此，只不过XSS一般注入的是恶意的脚本代码，这些脚本代码可以用来获取合法用户的数据，如Cookie信息。其原理如下图所示：XSS从攻击原理上，分为三类：1：反射型XSS 将用户输入“反射”回浏览器，即将用户的输入变成HTML传输回客户端。如： Response.Write(“&lt;script&amp;g</summary><published>2012-05-22T00:23:00Z</published><updated>2012-05-22T00:23:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2012/05/22/2507185.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2012/05/22/2507185.html"/><content type="html">&lt;p&gt;&lt;strong&gt;原理&lt;/strong&gt;&lt;br /&gt;跨站脚本（Cross site script，简称xss）是一种&amp;ldquo;HTML注入&amp;rdquo;，由于攻击的脚本多数时候是跨域的，所以称之为&amp;ldquo;跨域脚本&amp;rdquo;。&lt;/p&gt;&lt;p&gt;我们常常听到&amp;ldquo;注入&amp;rdquo;（Injection），如SQL注入，那么到底&amp;ldquo;注入&amp;rdquo;是什么？注入本质上就是把输入的数据变成可执行的程序语句。SQL注入是如此，XSS也如此，只不过XSS一般注入的是恶意的脚本代码，这些脚本代码可以用来获取合法用户的数据，如Cookie信息。&lt;/p&gt;&lt;p&gt;其原理如下图所示：&lt;/p&gt;&lt;p&gt;&lt;img src="http://pic002.cnblogs.com/images/2012/123061/2012051720493486.png" alt="" width="666" height="589" /&gt;&lt;/p&gt;&lt;p&gt;XSS从攻击原理上，分为三类：&lt;/p&gt;&lt;p&gt;1：反射型XSS&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 将用户输入&amp;ldquo;反射&amp;rdquo;回浏览器，即将用户的输入变成HTML传输回客户端。如：&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Response.Write(&amp;ldquo;&amp;lt;script&amp;gt;alert(/xss/);&amp;lt;/script&amp;gt;&amp;rdquo;)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;就是一个典型的反射型XSS。&lt;/p&gt;&lt;p&gt;2：存储性XSS&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 存储性XSS本质上也是一种反射型XSS，但是它把攻击脚本放置在服务器端，一旦被注入，可被多人多次利用。如，发表博文，就可以引入存储性的XSS。&lt;/p&gt;&lt;p&gt;3：DOM BASED XSS&lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如果用户的输入被用于修改原有HTML的DOM内容，就会引入这一类攻击。&amp;nbsp; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 最典型的是输入的内容用于作为某个节点的innerHTML，如果不对输入作验证，则会被注入攻击代码。&amp;nbsp; &lt;/p&gt;&lt;p&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; 如下的一段脚本注入后，就会获取用户的Cookie&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;script language=&amp;rdquo;javascript&amp;rdquo;&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; var cockieInfo =window.cockie;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; //send cockieInfo to luminji&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;lt;/javascript&amp;gt;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;br /&gt;实际案例&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;使用Fiddler，查看到某系统添加公共信息有一处Post，我们伪造如下的请求：&lt;/p&gt;&lt;p&gt;POST xxx.com/AddPublicInfo HTTP/1.1&lt;br /&gt;Accept: */*&lt;br /&gt;Accept-Language: zh-cn&lt;br /&gt;Content-Type: application/json; charset=utf-8&lt;br /&gt;Accept-Encoding: gzip, deflate&lt;br /&gt;Host: 192.168.80.136&lt;br /&gt;Content-Length: 187&lt;br /&gt;Cookie: ASP.NET_SessionId=qk1qvprjrikp2peveg2ini45; LanguageKey=zh_cn; LoginId=dean;&lt;/p&gt;&lt;p&gt;{"type":"ExtBulletin","title":"1111asdf11","content":"22sdfs22&amp;lt;script&amp;gt;alert(/xss/);&amp;lt;/script&amp;gt;","validPeriod":"1","beginDate":"","endDate":"","language":"None","linkFile":"0","fileName":""}&lt;/p&gt;&lt;p&gt;然后，刷新公共信息页面，发现注入代码成功。如果该站点同时存在会话劫持方法的漏洞，则将注入脚本改称获取cookie，攻击者就可以伪造任意访问了本信息的用户来登录系统。&lt;/p&gt;&lt;p&gt;通过该例子我们也可以看到，对于XSS的防范，所有的处理应该是在服务器端的，因为客户端的验证，如使用JS来验证输入是完全可以通过伪造请求被绕过的。所以在安全架构方面对于JS脚本的定位为：JS仅用于改善用户体验。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;应对策略&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;1. 在服务器段限制输入格式,输入类型，输入长度以及输入字符&lt;/p&gt;&lt;p&gt;要注意避免使用一些有潜在危险的html标签，这些标签很容易嵌入一些恶意网页代码。如&amp;lt;img&amp;gt; &amp;lt;iframe&amp;gt;&amp;lt;script&amp;gt;&amp;lt;frameset&amp;gt;&amp;lt;embed&amp;gt;&amp;lt;object&amp;gt;&amp;lt; style&amp;gt;等。&lt;/p&gt;&lt;p&gt;注意，不要仅仅在客户端使用js代码加以验证。因为客户端的js脚本可以被绕过。&lt;/p&gt;&lt;p&gt;2. 格式化输出。将输入的内容通过HttpUtility.HtmlEncode处理，这样就不能直接看出输出的内容。&lt;/p&gt;&lt;p&gt;3：对于asp.net站点，可以确保：&lt;/p&gt;&lt;p&gt;&amp;lt;configuration&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;system.web&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;pages&amp;nbsp;&amp;nbsp; validateRequest="true"&amp;nbsp;&amp;nbsp; /&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/system.web&amp;gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &lt;br /&gt;&amp;nbsp; &amp;lt;/configuration&amp;gt;&lt;/p&gt;&lt;p&gt;注意，默认情况下为true。&lt;/p&gt;&lt;p&gt;4：IE本身也有机制阻止跨站脚本&lt;/p&gt;&lt;p&gt;存在跨站脚本威胁，如果使用的IE8，则这个请求会被拦截，提示&amp;ldquo;Internet Explorer 已对此页面进行了修改，以帮助阻止跨站脚本。单击此处，获取详细信息...&amp;rdquo;。 &lt;/p&gt;&lt;p&gt;这个错误是由于 IE8 的跨站脚本(Cross-site scripting, XSS)防护阻止了跨站发送的请求。 &lt;/p&gt;&lt;p&gt;以下是如何配置它：点击 IE8 的&amp;ldquo;工具&amp;rdquo;-&amp;ldquo;Internet 选项&amp;rdquo;，进入&amp;ldquo;安全&amp;rdquo;选项卡，打开&amp;ldquo;Internet&amp;rdquo;下方的&amp;ldquo;自定义级别&amp;rdquo;，在&amp;ldquo;安全设置&amp;rdquo;对话框中找到&amp;ldquo;启用 XSS 筛选器&amp;rdquo;，改为&amp;ldquo;禁用&amp;rdquo;即可。 注意，&amp;ldquo;启用&amp;rdquo;是默认配置。&lt;/p&gt;&lt;p&gt;&amp;nbsp;当然，浏览器本身的这种机制是不可靠的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;&lt;br /&gt;具体措施&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有code behind中的&amp;rdquo;.Text&amp;rdquo;&lt;/p&gt;&lt;p&gt;通过文件查找出所有的为文本赋值的代码行；&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 为赋值Encode&lt;/p&gt;&lt;p&gt;对上一步骤查找出来的赋值，首先Encode。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有的WCF方法中返回字符串的&lt;/p&gt;&lt;p&gt;通过文件查找&amp;rdquo;.svc&amp;rdquo;，找到全部的WCF方法，筛选出返回string的方法&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 处理字符串再返回&lt;/p&gt;&lt;p&gt;对上一步骤查找出来的返回的string，先找到序列化方法，然后对实体的可能被用于输出为html的属性，首先Encode&lt;/p&gt;&lt;p&gt;以上统计纳入到如下表格：&lt;/p&gt;&lt;table style="width: 338px;" border="1" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="54"&gt;&lt;p&gt;序号&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="81"&gt;&lt;p&gt;文件&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="110"&gt;&lt;p&gt;代码行&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="93"&gt;&lt;p&gt;处理完成否&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="54"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="81"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="110"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="93"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有的文本输入控件&lt;/p&gt;&lt;p&gt;通过文件查找前台所有的&amp;rdquo;&amp;rdquo;text&amp;rdquo;&amp;rdquo;,&amp;rdquo;&amp;rsquo;text&amp;rsquo;&amp;rdquo;,&amp;rdquo;type=text&amp;rdquo;,&amp;rdquo;textarea&amp;rdquo;,&amp;rdquo;textbox&amp;rdquo;字样，筛选出含文本输入的控件，这些控件包括：&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;input type="text" /&amp;gt;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;textarea&amp;gt;&amp;lt;/textarea&amp;gt;&lt;/p&gt;&lt;p align="left"&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;asp:TextBox runat="server"&amp;gt;&amp;lt;/asp:TextBox&amp;gt;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有带参数的URL&lt;/p&gt;&lt;p&gt;通过文件查找所有的&amp;rdquo;aspx?&amp;rdquo;,&amp;rdquo;html?&amp;rdquo;,&amp;rdquo;htm?&amp;rdquo;,&amp;rdquo;.svc&amp;rdquo;（随系统的文件后缀方案而定），筛选出所有带参数的URL。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有的ajax&lt;/p&gt;&lt;p&gt;通过文件查找前台中所有的&amp;rdquo;ajax&amp;rdquo;，筛选出所有的ajax输入。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有cookie&lt;/p&gt;&lt;p&gt;通过文件查找所有的&amp;rdquo;cookie&amp;rdquo;，找出所有设置cookie的地方。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 查找所有session&lt;/p&gt;&lt;p&gt;通过文件查找所有的&amp;rdquo; session&amp;rdquo;，找出所有设置session的地方。&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 处理以上查找的结果&lt;/p&gt;&lt;p&gt;分别将其对应的输出查找出来，然后汇集为如下的表格：&lt;/p&gt;&lt;table style="width: 595px;" border="1" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="54"&gt;&lt;p&gt;序号&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="81"&gt;&lt;p&gt;前台文件&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="110"&gt;&lt;p&gt;输入出代码行&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="82"&gt;&lt;p&gt;输出文件&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="175"&gt;&lt;p&gt;为输出赋值的代码行&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="93"&gt;&lt;p&gt;处理完成否&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="54"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="81"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="110"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="82"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="175"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="93"&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;n&amp;nbsp; 查找遗漏&lt;/p&gt;&lt;p&gt;为了防止遗漏，需要直接查找输出，通过文件查找所有的&amp;rdquo;Response.Write&amp;rdquo;（后台）,&amp;rdquo; innerHTML&amp;rdquo;（前台）,&amp;rdquo;document.write&amp;rdquo;（前台）。将遗漏更新到上面的表格中。&lt;/p&gt;&lt;p&gt;注意，实际上，如果将应用程序变量和数据库内容赋值给前台控件，通过这种方式还是不能找到遗漏的，只能依赖于前面的输入的查找来一定程度上避免这种情况。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 根据以下表格中的处理方法来处理上面统计出来的表格中的代码行，为输出Encode&lt;/p&gt;&lt;p&gt;为了防范XSS攻击，我们可以使用Anti-Cross Site Scripting Library（查看&lt;a href="http://msdn.microsoft.com/en-us/library/aa973813.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa973813.aspx&lt;/a&gt;，不过当前版本已经到了4.2.1，下载地址为：http://www.microsoft.com/en-us/download/details.aspx?id=28589）&lt;/p&gt;&lt;p&gt;该Library提供如下的几个函数，分别对不同的应用场景作出处理：&lt;/p&gt;&lt;table style="width: 109%;" border="1" cellspacing="0" cellpadding="0"&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;Encoding Method&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Should Be Used If &amp;hellip;&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;Example/Pattern&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;HtmlEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used in HTML output except when assigning to an HTML attribute.&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;a href="http://www.contoso.com"&amp;gt;Click Here [Untrusted input]&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;HtmlAttributeEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used as an HTML attribute&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;hr noshade size=[Untrusted input]&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;JavaScriptEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used within a JavaScript context&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;hellip;&lt;/p&gt;&lt;p&gt;[Untrusted input]&lt;/p&gt;&lt;p&gt;&amp;hellip;&lt;/p&gt;&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;UrlEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used in a URL (such as a value in a querystring)&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;a href="http://search.msn.com/results.aspx?q=[Untrusted-input]"&amp;gt;Click Here!&amp;lt;/a&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;VisualBasicScriptEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used within a Visual Basic Script context&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;script type="text/vbscript" language="vbscript"&amp;gt;&lt;/p&gt;&lt;p&gt;&amp;hellip;&lt;/p&gt;&lt;p&gt;[Untrusted input]&lt;/p&gt;&lt;p&gt;&amp;hellip;&lt;/p&gt;&lt;p&gt;&amp;lt;/script&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;XmlEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used in XML output, except when assigning to an XML attribute&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;xml_tag&amp;gt;[Untrusted input]&amp;lt;/xml_tag&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td valign="top" width="143"&gt;&lt;p&gt;XmlAttributeEncode&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="185"&gt;&lt;p&gt;Untrusted input is used as an XML attribute&lt;/p&gt;&lt;/td&gt;&lt;td valign="top" width="288"&gt;&lt;p&gt;&amp;lt;xml_tag attribute=[Untrusted input]&amp;gt;Some Text&amp;lt;/xml_tag&amp;gt;&lt;/p&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 确保配置web.config中的validateRequest="true"&lt;/p&gt;&lt;p&gt;n&amp;nbsp; 确保Cookie的httponly&lt;/p&gt;&lt;p&gt;通过文件查找所有的&amp;rdquo;cookie&amp;rdquo;，找出所有设置cookie的地方，为用于认证的cookie设置如下的格式：&lt;/p&gt;&lt;p&gt;Set-Cookie: cookieName=cookieValue;httponly&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;参考:&lt;/p&gt;&lt;p align="left"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/ff649310.aspx"&gt;http://msdn.microsoft.com/en-us/library/ff649310.aspx&lt;/a&gt;&lt;/p&gt;&lt;p align="left"&gt;&lt;a href="http://msdn.microsoft.com/en-us/library/aa973813.aspx"&gt;http://msdn.microsoft.com/en-us/library/aa973813.aspx&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2507185.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2012/05/22/2507185.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2012/05/16/2184280.html</id><title type="text">ASP.NET性能优化之负载均衡</title><summary type="text">1：HTTP重定向所谓HTTP重定向，就是通过修改HTTP响应头中的Location标识为新的URL，然后返回给客户端，让客户端重新根据这个Location标识的URL去做新的请求。这是一种最简单、也是最轻量级的负载均衡实现方案，使用asp.net，我们可以这样来实现，比如在主站www.yourdomain.com中，我们在默认主页如下编码： static string[] servers = { "http://192.168.0.77/luminji2/aspx/test3.aspx", ...</summary><published>2012-05-16T00:55:00Z</published><updated>2012-05-16T00:55:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2012/05/16/2184280.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2012/05/16/2184280.html"/><content type="html">&lt;p&gt;&lt;strong&gt;1：HTTP重定向&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;所谓HTTP重定向，就是通过修改HTTP响应头中的Location标识为新的URL，然后返回给客户端，让客户端重新根据这个Location标识的URL去做新的请求。&lt;/p&gt;&lt;p&gt;这是一种最简单、也是最轻量级的负载均衡实现方案，使用asp.net，我们可以这样来实现，比如在主站&lt;a href="http://www.yourdomain.com"&gt;www.yourdomain.com&lt;/a&gt;中，我们在默认主页如下编码：&lt;/p&gt;        static string[] servers = &lt;br/&gt;                {&lt;br/&gt;                    "http://192.168.0.77/luminji2/aspx/test3.aspx",&lt;br/&gt;                    "http://192.168.0.77/luminji2/aspx/test4.aspx"&lt;br/&gt;                };&lt;br/&gt;        protected void Page_Load(object sender, EventArgs e)&lt;br/&gt;        {&lt;br/&gt;            Response.Redirect(servers[DateTime.Now.Millisecond % 2]);&lt;br/&gt;        }&lt;p&gt;在上面的代码中，Response.Redirect实际为http头返回状态码302，这是为了告诉浏览器，请到Location中去拿URL，并且去到这个新的URL去做请求。当然，我们也可以采用最原始的方法来代替Redirect方法：&lt;/p&gt;            Response.Status = "302 Found";&lt;br/&gt;            Response.StatusCode = 302;&lt;br/&gt;            Response.AddHeader("Location", servers[DateTime.Now.Millisecond % 2]);&lt;p&gt;使用HttpWatch监视，我们对&lt;a href="http://www.yourdomain.com"&gt;www.yourdomain.com&lt;/a&gt;请求，得到：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109211738241096.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109211738261520.png" alt="image" width="757" height="317" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;可以清晰的看到第一次请求返回的302，然后转发到新的地址，得到状态码200。&lt;/p&gt;&lt;p&gt;以上方法是在客户端的重定向，即浏览器请求了两次，一次是到主服务器，第二次是到Location中指定的服务器上去请求。&lt;/p&gt;&lt;p&gt;HTTP重定向的方式非常依赖于主站的处理能力，它的性能瓶颈也是来自于IIS对于接受请求-&amp;gt;asp.net处理首页动态程序-&amp;gt;返回带有特定头请求，是的，它不能突破自身的性能瓶颈，比如，在我的破测试机上，我得到的吞吐率为：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109211738266819.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109211738276404.png" alt="image" width="402" height="211" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;好在IIS自身已经支持重定向（查阅&lt;a href="http://technet.microsoft.com/zh-cn/library/cc732969(WS.10).aspx"&gt;http://technet.microsoft.com/zh-cn/library/cc732969(WS.10).aspx&lt;/a&gt;），这更进一步省略了我们自己写代码实现重定向，省略运行ASP.NET代码带来的性能损耗。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;2：varnish实现的反向代理负载均衡&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;另外一种思路是使用反向代理服务器的负载均衡功能，上篇当中介绍的varnish就支持这样的功能，查看配置文件：&lt;/p&gt;backend web1 {&lt;br/&gt;     .host = "192.168.0.77";&lt;br/&gt;     .port = "8081";     &lt;br/&gt;}&lt;br/&gt;backend web2 {&lt;br/&gt;     .host = "192.168.0.77";&lt;br/&gt;     .port = "8082";&lt;br/&gt;}&lt;br/&gt;director lb round-robin {&lt;br/&gt;{&lt;br/&gt;.backend = web1;&lt;br/&gt;}&lt;br/&gt;{&lt;br/&gt;.backend = web2;&lt;br/&gt;}&lt;br/&gt;}&lt;br/&gt; sub vcl_recv {&lt;br/&gt;     set req.backend = lb;&lt;br/&gt;     return (pass);&lt;br/&gt; }&lt;p&gt;在该配置文件中，我们部署了两台WEB服务器，当然，为了简单期间，我这里是使用了同一台服务器的两个端口。在vcl_recv函数中，varnish定义了负载均衡。&lt;/p&gt;&lt;p&gt;运行varnish之，我们会发现请求被转发到后台服务器了。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;3：其它方案&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;1：DNS负载均衡，通过增加域名A记录来让DNS服务器实现负载均衡。好处是几乎不会碰到性能问题。缺点：要求每个WEB服务器必须有外网地址。一旦某台服务器崩溃，不能及时让DNS修改生效。不能定义自己的转发策略；&lt;/p&gt;&lt;p&gt;2：IP负载均衡，有LVS-NAT，采用iptables，对LINUX内核操作，性能相对于反向代理服务器并没有质的飞跃；IP负载均衡仍旧需要转发请求给实际服务器，同时需要转发实际服务器的响应给用户，所以，它的性能瓶颈来自于NAT服务器的性能及网络带宽；&lt;/p&gt;&lt;p&gt;3：直接路由，有LVS-DR，工作在数据链路层（第二层），要求所有WEB服务器接入外网；负载均衡器负责转发请求给实际服务器，但是它通过修改数据包中的MAC地址，能够做到让实际服务器的响应直接返回给用户，而不用通过负载均衡器，这当然进一步提升了负载均衡的效率；&lt;/p&gt;&lt;p&gt;4：IP隧道，有LVS-TUN，用于不同机房（即不同WAN网段）的负载均衡，原理同LVS-DR；&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2184280.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2012/05/16/2184280.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2012/01/13/2321896.html</id><title type="text">基于Unity的AOP的符合基于角色的访问控制（RBAC）模型的通用权限设计</title><summary type="text">AOP的特性使得它非常适合用来设计类似权限控制的功能，这是本文的基础，如果想要了解AOP的实现，可以参考《动态织入的AOP实现》。 在基于角色的访问控制（RBAC）中，有三要素：用户、角色、任务（或操作）（User、Role、Task），其稳定性逐渐增强，两个关系，User&lt;-&gt;Role、Role&lt;-&gt;Task，其中： User 是日常管理运行时建立 Role 是部署/交付建立 ...</summary><published>2012-01-13T09:38:00Z</published><updated>2012-01-13T09:38:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2012/01/13/2321896.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2012/01/13/2321896.html"/><content type="html">&lt;p&gt;AOP的特性使得它非常适合用来设计类似权限控制的功能，这是本文的基础，如果想要了解AOP的实现，可以参考《&lt;a href="http://www.cnblogs.com/luminji/archive/2012/01/10/2318211.html"&gt;动态织入的AOP实现&lt;/a&gt;》。&lt;/p&gt;  &lt;p&gt;在基于角色的访问控制（RBAC）中，有三要素：用户、角色、任务（或操作）（User、Role、Task），其稳定性逐渐增强，两个关系，User&amp;lt;-&amp;gt;Role、Role&amp;lt;-&amp;gt;Task，其中：&lt;/p&gt;  &lt;ul&gt;   &lt;li&gt;User 是日常管理运行时建立 &lt;/li&gt;    &lt;li&gt;Role 是部署/交付建立 &lt;/li&gt;    &lt;li&gt;Task 是开发时确定 &lt;/li&gt;    &lt;li&gt;User&amp;lt;-&amp;gt;Role 是日常管理运行时建立 &lt;/li&gt;    &lt;li&gt;Role&amp;lt;-&amp;gt;Task 是部署/交付时建立 &lt;/li&gt; &lt;/ul&gt;  &lt;p&gt;在本例中，针对Task和Role，我们设计如下的两个类：&lt;/p&gt;      [AttributeUsage(AttributeTargets.All, AllowMultiple = false, Inherited = true)]&lt;br/&gt;    public class TaskAttribute: Attribute&lt;br/&gt;    {&lt;br/&gt;&lt;br/&gt;        public TaskAttribute(string taskName, string taskDescription)&lt;br/&gt;        {&lt;br/&gt;            TaskName = taskName;&lt;br/&gt;            TaskDescription = taskDescription;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public string TaskName { get; set; }&lt;br/&gt;        public string TaskDescription { get; set; }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public class Role&lt;br/&gt;    {&lt;br/&gt;        public string Name { get; set; }&lt;br/&gt;        public List&amp;lt;TaskAttribute&amp;gt; Tasks { get; set; }&lt;br/&gt;    }&lt;p&gt;可以看到，Task是继承自Attribute的，源于Task需要和实际的功能接口匹配起来，而Role，则无此需要。&lt;/p&gt;&lt;p&gt;本文演示所需要的权限关系描述如下：&lt;/p&gt;&lt;p&gt;1：系统有4个权限；&lt;/p&gt;&lt;p&gt;2：系统有两个角色，一个叫做Manager，它具有两个权限，另一个角色为Common，它当前不具备任何权限；&lt;/p&gt;&lt;p&gt;以上的关系描述，我们在代码当中模拟如下：&lt;/p&gt;        //模拟系统总共有4种权限&lt;br/&gt;        public static List&amp;lt;TaskAttribute&amp;gt; Tasks&lt;br/&gt;        {&lt;br/&gt;            get&lt;br/&gt;            {&lt;br/&gt;                if (_tasks == null)&lt;br/&gt;                {&lt;br/&gt;                    _tasks = new List&amp;lt;TaskAttribute&amp;gt;()&lt;br/&gt;                                 {&lt;br/&gt;                                     new TaskAttribute(&amp;quot;AddItem&amp;quot;,&amp;quot;增加&amp;quot;),&lt;br/&gt;                                     new TaskAttribute(&amp;quot;ModifyItem&amp;quot;,&amp;quot;修改&amp;quot;),&lt;br/&gt;                                     new TaskAttribute(&amp;quot;RemoveItem&amp;quot;,&amp;quot;删除&amp;quot;),&lt;br/&gt;                                     new TaskAttribute(&amp;quot;ListItem&amp;quot;,&amp;quot;获取列表&amp;quot;)&lt;br/&gt;                                 };&lt;br/&gt;                }&lt;br/&gt;                return _tasks;&lt;br/&gt;            }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        private static List&amp;lt;Role&amp;gt; _roles;&lt;br/&gt;&lt;br/&gt;        //模拟系统总共有两类角色&lt;br/&gt;        //第一类角色Manager，有增加和修改权限&lt;br/&gt;        //第二类角色Common，没有任何权限&lt;br/&gt;        public static List&amp;lt;Role&amp;gt; Roles&lt;br/&gt;        {&lt;br/&gt;            get&lt;br/&gt;            {&lt;br/&gt;                if (_roles == null)&lt;br/&gt;                {&lt;br/&gt;                    _roles = new List&amp;lt;Role&amp;gt;()&lt;br/&gt;                                {&lt;br/&gt;                                    new Role(){Name = &amp;quot;Manager&amp;quot;, Tasks = new List&amp;lt;TaskAttribute&amp;gt;()&lt;br/&gt;                                                                      {&lt;br/&gt;                                                                            new TaskAttribute(&amp;quot;AddItem&amp;quot;,&amp;quot;增加&amp;quot;),&lt;br/&gt;                                                                            new TaskAttribute(&amp;quot;ModifyItem&amp;quot;,&amp;quot;修改&amp;quot;)&lt;br/&gt;                                                                      }},&lt;br/&gt;                                    new Role(){Name = &amp;quot;Common&amp;quot;, Tasks = new List&amp;lt;TaskAttribute&amp;gt;()}&lt;br/&gt;                                };&lt;br/&gt;                }&lt;br/&gt;                return _roles;&lt;br/&gt;            }&lt;br/&gt;        }&lt;p&gt;权限判断在切面部分，简化如下（可以看到是判断当前用户是否具有相关权限）：&lt;/p&gt;    public class AuthorityHandler : ICallHandler&lt;br/&gt;    {&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// Invoke order&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        public int Order { get; set; }&lt;br/&gt;        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)&lt;br/&gt;        {&lt;br/&gt;            MethodBase mb = input.MethodBase;&lt;br/&gt;            object[] attrObj = mb.GetCustomAttributes(typeof(TaskAttribute), false);&lt;br/&gt;&lt;br/&gt;            if (attrObj == null)&lt;br/&gt;            {&lt;br/&gt;                throw new ArgumentException(&amp;quot;TaskAttribute should be defined with the AuthorityAttribute&amp;quot;);&lt;br/&gt;            }&lt;br/&gt;            else&lt;br/&gt;            {&lt;br/&gt;                TaskAttribute attr = (TaskAttribute)attrObj[0];&lt;br/&gt;                if (!string.IsNullOrEmpty(attr.TaskName))&lt;br/&gt;                {&lt;br/&gt;                    string taskName = attr.TaskName;&lt;br/&gt;                    //get current user's roles&lt;br/&gt;                    IEnumerable&amp;lt;Role&amp;gt; currentUserRoles = from p in SampleApp.Roles where p.Name == SampleApp.User.Name select p;&lt;br/&gt;                    //if match then return;&lt;br/&gt;                    foreach (Role currentUserRole in currentUserRoles)&lt;br/&gt;                    {&lt;br/&gt;                        IEnumerable&amp;lt;TaskAttribute&amp;gt; tasks = from p in currentUserRole.Tasks&lt;br/&gt;                                                           where p.TaskName == taskName&lt;br/&gt;                                                           select p;&lt;br/&gt;                        if (tasks.Count() &amp;gt; 0)&lt;br/&gt;                        {&lt;br/&gt;                            var retvalue = getNext()(input, getNext);&lt;br/&gt;                            return retvalue;&lt;br/&gt;                        }&lt;br/&gt;                    }&lt;br/&gt;                    //else throw exception&lt;br/&gt;                    throw new UnauthorizedAccessException(&amp;quot;access denied&amp;quot;);&lt;br/&gt;                }&lt;br/&gt;            }&lt;br/&gt;            return null;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public class AuthorityAttribute : HandlerAttribute&lt;br/&gt;    {&lt;br/&gt;        public override ICallHandler CreateHandler(IUnityContainer container)&lt;br/&gt;        {&lt;br/&gt;            return new AuthorityHandler();&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;调用方代码：&lt;/p&gt;        static void Main() {&lt;br/&gt;            var container1 = new UnityContainer()&lt;br/&gt;                .AddNewExtension&amp;lt;Interception&amp;gt;()&lt;br/&gt;                .RegisterType&amp;lt;IBiz, Biz1&amp;gt;();&lt;br/&gt;            container1&lt;br/&gt;                .Configure&amp;lt;Interception&amp;gt;()&lt;br/&gt;                .SetInterceptorFor&amp;lt;IBiz&amp;gt;(new InterfaceInterceptor());&lt;br/&gt;&lt;br/&gt;            SampleApp.User = new User() { Name = &amp;quot;Common&amp;quot; };&lt;br/&gt;            var sample1 = container1.Resolve&amp;lt;IBiz&amp;gt;();&lt;br/&gt;            sample1.AddItem();&lt;br/&gt;            &lt;br/&gt;            Console.ReadKey();&lt;br/&gt;        }&lt;p&gt;可以看到，使用了Unity来进行AOP；&lt;/p&gt;&lt;p&gt;运行效果：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201201/201201131736326606.png"&gt;&lt;img style="background-image: none; border-right-width: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201201/201201131736325700.png" width="531" height="35" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;代码下载：&lt;a href="http://files.cnblogs.com/luminji/%E6%9D%83%E9%99%90.rar"&gt;权限.rar&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2321896.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2012/01/13/2321896.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2012/01/10/2318211.html</id><title type="text">动态织入的AOP实现</title><summary type="text">动态织入的AOP实现，有两种方法：第一类，借助于Remoting命名空间下的几个类，通过获取当前上下文及反射的机制来实现，这需要被AOP的类需要继承自arshalByRefObject或者ContextBoundObject；第二类，原理是基于动态代理的思想，即在运行时动态构造一个原有类的子类，这样就可以在子类的重载方法中插入额外代码。这两类方法，都有显著的不足，前者直接要求我们继承固定类，后者呢，除非父类方法被定义为virtual，或者方法定义于某个接口，否则就不能被重载，这就是得“拦截”并不是可以对任意的方法进行的。动态织入局限于CLR的限制，不能实现对任何方法进行AOP，如果要突破这个限</summary><published>2012-01-10T07:48:00Z</published><updated>2012-01-10T07:48:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2012/01/10/2318211.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2012/01/10/2318211.html"/><content type="html">&lt;p&gt;动态织入的AOP实现，有两种方法：&lt;/p&gt;&lt;p&gt;第一类，借助于Remoting命名空间下的几个类，通过获取当前上下文及反射的机制来实现，这需要被AOP的类需要继承自&lt;code&gt;arshalByRefObject&lt;/code&gt;&lt;code&gt;或者ContextBoundObject；&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;第二类，原理是基于动态代理的思想，即在运行时动态构造一个原有类的子类，这样就可以在子类的重载方法中插入额外代码。&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;这两类方法，都有显著的不足，前者直接要求我们继承固定类，后者呢，除非父类方法被定义为virtual，或者方法定义于某个接口，否则就不能被重载，这就是得&amp;ldquo;拦截&amp;rdquo;并不是可以对任意的方法进行的。&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;动态织入局限于CLR的限制，不能实现对任何方法进行AOP，如果要突破这个限制，只能采用静态织入的方法，静态织入采用。静态织入突破OO设计模式，可以拦截所有的方法甚至构造函数或属性访问器，因为它是直接修改IL。还有，因为它在运行前修改原有程序集，也就基本不存在运行时的性能损失问题了。它的不足，一方面是框架较复杂，实现较麻烦，依赖于对底层的IL指令集的操纵；&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;&lt;strong&gt;一：继承自ContextBoundObject的实现&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;帮助类：&lt;/code&gt;&lt;/p&gt;    public class SecurityAspect : IMessageSink&lt;br/&gt;    {&lt;br/&gt;        internal SecurityAspect(IMessageSink next)&lt;br/&gt;        {&lt;br/&gt;            _next = next;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        private IMessageSink _next;&lt;br/&gt;&lt;br/&gt;        public IMessageSink NextSink&lt;br/&gt;        {&lt;br/&gt;            get { return _next; }&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public IMessage SyncProcessMessage(IMessage msg)&lt;br/&gt;        {&lt;br/&gt;            Preprocess(msg);&lt;br/&gt;            IMessage returnMethod = _next.SyncProcessMessage(msg);&lt;br/&gt;            return returnMethod;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public IMessageCtrl AsyncProcessMessage(IMessage msg, IMessageSink replySink)&lt;br/&gt;        {&lt;br/&gt;            throw new InvalidOperationException();&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        private void Preprocess(IMessage msg)&lt;br/&gt;        {&lt;br/&gt;            if (!(msg is IMethodMessage)) &lt;br/&gt;                return;&lt;br/&gt;            IMethodMessage call = msg as IMethodMessage;&lt;br/&gt;            Type type = Type.GetType(call.TypeName);&lt;br/&gt;            string callStr = type.Name + "." + call.MethodName;&lt;br/&gt;            Console.WriteLine("Security validating : {0} for {1}", callStr,&lt;br/&gt;                Environment.UserName);&lt;br/&gt;            // call some security validating code&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public class SecurityProperty : IContextProperty, IContributeObjectSink&lt;br/&gt;    {&lt;br/&gt;        public IMessageSink GetObjectSink(MarshalByRefObject o, IMessageSink next)&lt;br/&gt;        {&lt;br/&gt;            return new SecurityAspect(next);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public string Name&lt;br/&gt;        {&lt;br/&gt;            get { return "SecurityProperty"; }&lt;br/&gt;        }&lt;br/&gt;        public void Freeze(Context newContext)&lt;br/&gt;        {&lt;br/&gt;        }&lt;br/&gt;        public bool IsNewContextOK(Context newCtx)&lt;br/&gt;        {&lt;br/&gt;            return true;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    [AttributeUsage(AttributeTargets.All)]&lt;br/&gt;    public class SecurityAttribute : ContextAttribute&lt;br/&gt;    {&lt;br/&gt;        public SecurityAttribute() : base("Security") { }&lt;br/&gt;        public override void GetPropertiesForNewContext(IConstructionCallMessage ccm)&lt;br/&gt;        {&lt;br/&gt;            ccm.ContextProperties.Add(new SecurityProperty());&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;&lt;code&gt;调用方：&lt;/code&gt;&lt;/p&gt;    class Program&lt;br/&gt;    {&lt;br/&gt;        static void Main(string[] args)&lt;br/&gt;        {&lt;br/&gt;            SampleClass s = new SampleClass();&lt;br/&gt;            s.DoSomething();&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;&lt;br/&gt;    [Security]&lt;br/&gt;    [Tracing]&lt;br/&gt;    public class SampleClass: ContextBoundObject&lt;br/&gt;    {&lt;br/&gt;        public void DoSomething()&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("do something");&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;&lt;code&gt;&lt;strong&gt;二：Virtual方法及接口的实现&lt;/strong&gt;&lt;/code&gt;&lt;/p&gt;&lt;p&gt;&lt;code&gt;帮助类：&lt;/code&gt;&lt;/p&gt;    public class LogHandler : ICallHandler&lt;br/&gt;    {&lt;br/&gt;        /// &amp;lt;summary&amp;gt;&lt;br/&gt;        /// 执行顺序&lt;br/&gt;        /// &amp;lt;/summary&amp;gt;&lt;br/&gt;        public int Order { get; set; }&lt;br/&gt;        public IMethodReturn Invoke(IMethodInvocation input, GetNextHandlerDelegate getNext)&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("方法名: {0}", input.MethodBase.Name);&lt;br/&gt;            Console.WriteLine("参数:");&lt;br/&gt;            for (var i = 0; i &amp;lt; input.Arguments.Count; i++)&lt;br/&gt;            {&lt;br/&gt;                Console.WriteLine("{0}: {1}", input.Arguments.ParameterName(i), input.Arguments[i]);&lt;br/&gt;            }&lt;br/&gt;            Console.WriteLine("方法执行前的处理");&lt;br/&gt;            var retvalue = getNext()(input, getNext);&lt;br/&gt;            Console.WriteLine("方法执行后的处理");&lt;br/&gt;            return retvalue;&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public class LogHandlerAttribute : HandlerAttribute&lt;br/&gt;    {&lt;br/&gt;        public override ICallHandler CreateHandler(IUnityContainer container)&lt;br/&gt;        {&lt;br/&gt;            return new LogHandler();&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;&lt;code&gt;调用方：&lt;/code&gt;&lt;/p&gt;    public interface ISample&lt;br/&gt;    {&lt;br/&gt;        [LogHandler]&lt;br/&gt;        void DoSomething();&lt;br/&gt;        void DoSomethingNoAop();&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    class Sample1 : ISample&lt;br/&gt;    {&lt;br/&gt;        public void DoSomething()&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("Sample1 do something");&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public void DoSomethingNoAop()&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("Sample1 do something no aop");&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    public class SampleClass&lt;br/&gt;    {&lt;br/&gt;&lt;br/&gt;        [LogHandler]&lt;br/&gt;        public virtual void SampleVirtual()&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("Virtual method");&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public void Sample()&lt;br/&gt;        {&lt;br/&gt;            Console.WriteLine("Sampe method");&lt;br/&gt;        }&lt;br/&gt;    }&lt;br/&gt;&lt;br/&gt;    class Program {&lt;br/&gt;&lt;br/&gt;        static void Main() {&lt;br/&gt;            //针对接口&lt;br/&gt;            var container1 = new UnityContainer()&lt;br/&gt;                .AddNewExtension&amp;lt;Interception&amp;gt;()&lt;br/&gt;                .RegisterType&amp;lt;ISample, Sample1&amp;gt;();&lt;br/&gt;            container1&lt;br/&gt;                .Configure&amp;lt;Interception&amp;gt;()&lt;br/&gt;                .SetInterceptorFor&amp;lt;ISample&amp;gt;(new InterfaceInterceptor());&lt;br/&gt;            container1&lt;br/&gt;                .Configure&amp;lt;Interception&amp;gt;()&lt;br/&gt;                .SetInterceptorFor&amp;lt;SampleClass&amp;gt;(new VirtualMethodInterceptor());&lt;br/&gt;            var sample1 = container1.Resolve&amp;lt;ISample&amp;gt;();&lt;br/&gt;            sample1.DoSomething();&lt;br/&gt;            sample1.DoSomethingNoAop();&lt;br/&gt;&lt;br/&gt;            //针对虚拟方法&lt;br/&gt;            var sample2 = container1.Resolve&amp;lt;SampleClass&amp;gt;();&lt;br/&gt;            sample2.SampleVirtual();&lt;br/&gt;            sample2.Sample();&lt;br/&gt;&lt;br/&gt;            Console.ReadKey();&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;    }&lt;p&gt;&lt;code&gt;可以看到，第二种方法是用Unity实现的，关于Unity，这里多说两句：&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Unity的AOP可以从3种标记的情况拦截：&lt;br /&gt;TransparentProxyInterceptor：直接在类的方法上进行标记，但是这个类必须继承MarshalByRefObject；&lt;br /&gt;VirtualMethod：直接在类的虚方法上进行标记，如上文代码；&lt;br /&gt;InterfaceInterceptor：在接口的方法上进行标记，如上文代码；&lt;/p&gt;&lt;p&gt;&lt;code&gt;代码下载：&lt;a href="http://files.cnblogs.com/luminji/ConsoleApplication1.rar"&gt;ConsoleApplication1.rar&lt;/a&gt;，&lt;a href="http://files.cnblogs.com/luminji/ConsoleApplication2.rar"&gt;ConsoleApplication2.rar&lt;/a&gt;&lt;/code&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2318211.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2012/01/10/2318211.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2011/12/22/2298262.html</id><title type="text">MVC TIP8：为控制器增加有参构造函数（为了注入等其它用途）</title><summary type="text">控制器本身是不带有参的构造函数的，如果我们为控制器仅仅提供有参的构造函数，就会报错。不过，可以利用DependencyResolver的SetResolver方法，让ASP.NET MVC支持有参的构造函数。 1：为系统准备两个类型，如下： 注意，这里要实现的是让控制器支持Unity注入。 UnityControllerFactory代码如下： public class Uni...</summary><published>2011-12-22T10:04:00Z</published><updated>2011-12-22T10:04:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2011/12/22/2298262.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2011/12/22/2298262.html"/><content type="html">&lt;p&gt;控制器本身是不带有参的构造函数的，如果我们为控制器仅仅提供有参的构造函数，就会报错。不过，可以利用DependencyResolver的SetResolver方法，让ASP.NET MVC支持有参的构造函数。&lt;/p&gt;  &lt;p&gt;&lt;strong&gt;1：为系统准备两个类型，如下：&lt;/strong&gt;&lt;/p&gt;  &lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112221803448555.png"&gt;&lt;img style="background-image: none; border-bottom: 0px; border-left: 0px; padding-left: 0px; padding-right: 0px; display: inline; border-top: 0px; border-right: 0px; padding-top: 0px" title="image" border="0" alt="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112221803457225.png" width="247" height="301" /&gt;&lt;/a&gt;&lt;/p&gt;  &lt;p&gt;注意，这里要实现的是让控制器支持Unity注入。&lt;/p&gt;  &lt;p&gt;UnityControllerFactory代码如下：&lt;/p&gt;      public class UnityControllerFactory : DefaultControllerFactory&lt;br/&gt;    {&lt;br/&gt;        IUnityContainer container;&lt;br/&gt;        public UnityControllerFactory(IUnityContainer container)&lt;br/&gt;        {&lt;br/&gt;            this.container = container;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        protected override IController GetControllerInstance(RequestContext reqContext,&lt;br/&gt;            Type controllerType)&lt;br/&gt;        {&lt;br/&gt;            if (controllerType == null)&lt;br/&gt;            {&lt;br/&gt;                return null;&lt;br/&gt;            }&lt;br/&gt;            return container.Resolve(controllerType) as IController;&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;UnityDependencyResolver代码如下：&lt;/p&gt;    public class UnityDependencyResolver : IDependencyResolver&lt;br/&gt;    {&lt;br/&gt;        IUnityContainer container;&lt;br/&gt;&lt;br/&gt;        public UnityDependencyResolver(IUnityContainer container)&lt;br/&gt;        {&lt;br/&gt;            this.container = container;&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public object GetService(Type serviceType)&lt;br/&gt;        {&lt;br/&gt;            if (!this.container.IsRegistered(serviceType))&lt;br/&gt;            {&lt;br/&gt;                return null;&lt;br/&gt;            }&lt;br/&gt;            return container.Resolve(serviceType);&lt;br/&gt;        }&lt;br/&gt;&lt;br/&gt;        public IEnumerable&amp;lt;object&amp;gt; GetServices(Type serviceType)&lt;br/&gt;        {&lt;br/&gt;            return container.ResolveAll(serviceType);&lt;br/&gt;        }&lt;br/&gt;    }&lt;p&gt;&lt;strong&gt;2：在Application_Start中注册&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;代码如下：&lt;/p&gt;            using (IUnityContainer container = new UnityContainer())&lt;br/&gt;            {&lt;br/&gt;                UnityConfigurationSection section = (UnityConfigurationSection)ConfigurationManager.GetSection(&amp;quot;unity&amp;quot;);&lt;br/&gt;                section.Configure(container, &amp;quot;containerOne&amp;quot;);&lt;br/&gt;                //tell mvc use UnityDependencyResolver to create controll instances;&lt;br/&gt;                container.RegisterType&amp;lt;IControllerFactory, UnityControllerFactory&amp;gt;();&lt;br/&gt;                //_container.RegisterType&amp;lt;ISubjectRepository, SubjectRepository&amp;gt;(new TransientLifetimeManager());&lt;br/&gt;                DependencyResolver.SetResolver(new UnityDependencyResolver(container));&lt;br/&gt;            }&lt;p&gt;&lt;strong&gt;3：Web.config配置如下&lt;/strong&gt;&lt;/p&gt;  &amp;lt;configSections&amp;gt;&lt;br/&gt;    &amp;lt;section name=&amp;quot;unity&amp;quot; type=&amp;quot;Microsoft.Practices.Unity.Configuration.UnityConfigurationSection,Microsoft.Practices.Unity.Configuration&amp;quot;/&amp;gt;&lt;br/&gt;  &amp;lt;/configSections&amp;gt;&lt;br/&gt;&lt;br/&gt;  &amp;lt;unity xmlns=&amp;quot;http://schemas.microsoft.com/practices/2010/unity&amp;quot;&amp;gt;&lt;br/&gt;    &amp;lt;assembly name=&amp;quot;SampleProject&amp;quot;/&amp;gt;&lt;br/&gt;    &amp;lt;namespace name=&amp;quot;SampleProject&amp;quot;/&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;alias alias=&amp;quot;ISampleClass&amp;quot; type=&amp;quot;ISampleClass&amp;quot; /&amp;gt;&lt;br/&gt;    &amp;lt;alias alias=&amp;quot;SampleClass&amp;quot; type=&amp;quot;SampleClass&amp;quot; /&amp;gt;&lt;br/&gt;&lt;br/&gt;    &amp;lt;container name=&amp;quot;containerOne&amp;quot;&amp;gt;&lt;br/&gt;      &amp;lt;register type=&amp;quot;ISampleClass&amp;quot; mapTo=&amp;quot;SampleClass&amp;quot; /&amp;gt;&lt;br/&gt;    &amp;lt;/container&amp;gt;&lt;br/&gt;&lt;br/&gt;  &amp;lt;/unity&amp;gt;&lt;p&gt;至此，完成本文所述功能。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2298262.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2011/12/22/2298262.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2011/12/16/2290313.html</id><title type="text">最精简领域驱动设计开发模版（针对WPF）</title><summary type="text">一：领域驱动中的分层领域驱动设计将软件系统分为四层：基础结构层、领域层、应用层和表现层。· 基础结构层：该层专为其它各层提供技术框架支持。注意，这部分内容不会涉及任何业务知识。众所周知的数据访问的内容，也被放在了该层当中，因为数据的读写是业务无关的。· 领域层：包含了业务所涉及的领域对象（实体、值对象）、领域服务以及它们之间的关系。这部分内容的具体表现形式就是领域模型（Domain Model）。领域驱动设计提倡富领域模型，即尽量将业务逻辑归属到领域对象上，实在无法归属的部分则以领域服务的形式进行定义。· 应用层：该层不包含任何领域逻辑，但它会对任务进行协调，并可</summary><published>2011-12-16T07:57:00Z</published><updated>2011-12-16T07:57:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2011/12/16/2290313.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2011/12/16/2290313.html"/><content type="html">&lt;p&gt;&lt;strong&gt;一：领域驱动中的分层&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;领域驱动设计将软件系统分为四层：基础结构层、领域层、应用层和表现层。&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/daxnet/WindowsLiveWriter/EntityFramework_7091/52029421305_2.gif"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="clip_image001" src="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112161556563012.png" alt="clip_image001" width="329" height="255" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;middot; 基础结构层：该层专为其它各层提供技术框架支持。注意，这部分内容不会涉及任何业务知识。众所周知的数据访问的内容，也被放在了该层当中，因为数据的读写是业务无关的。&lt;/p&gt;&lt;p&gt;&amp;middot; 领域层：包含了业务所涉及的领域对象（实体、值对象）、领域服务以及它们之间的关系。这部分内容的具体表现形式就是领域模型（Domain Model）。领域驱动设计提倡富领域模型，即尽量将业务逻辑归属到领域对象上，实在无法归属的部分则以领域服务的形式进行定义。&lt;/p&gt;&lt;p&gt;&amp;middot; 应用层：该层不包含任何领域逻辑，但它会对任务进行协调，并可以维护应用程序的状态，因此，它更注重流程性的东西。&lt;/p&gt;&lt;p&gt;&amp;middot; 表现层：这个好理解，跟三层里的表现层意思差不多，但请注意，&amp;ldquo;Web服务&amp;rdquo;虽然是服务，但它是表现层的东西&lt;/p&gt;&lt;p&gt;从上图还可以看到，表现层与应用层之间是通过数据传输对象（DTO）进行交互的，数据传输对象是没有行为的POCO对象，它的目的只是为了对领域对象进行数据封装，实现层与层之间的数据传递。为何不能直接将领域对象用于数据传递？因为领域对象更注重领域，而DTO更注重数据。不仅如此，由于&amp;ldquo;富领域模型&amp;rdquo;的特点，这样做会直接将领域对象的行为暴露给表现层。&lt;/p&gt;&lt;p&gt;（在本例中，为了简便期间，用了领域模型，而不是DTO，DTO可以放在基础结构层）&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二：实例分层&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112161556573111.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112161556587736.png" alt="image" width="820" height="843" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;可以看到：&lt;/p&gt;&lt;p&gt;基础结构层：SmartCA.Infrastructure，包括了一些最简单的声明和实现，如实体对象工厂接口、数据接口、工作单元接口及实现、数据对象的基类；SmartCA.Infrastructure.Repositories，数据访问及实体对象工厂，如果我们换一个数据库，则可以增加第二个Repositories项目；&lt;/p&gt;&lt;p&gt;领域层：领域模型和领域服务在这里；&lt;/p&gt;&lt;p&gt;应用层：在本例子中，应用层只用来存储应用程序的某些状态，不涉及到任何领域领域逻辑；&lt;/p&gt;&lt;p&gt;表现层：无任何特殊；&lt;/p&gt;&lt;p&gt;&lt;strong&gt;三：核心类图&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112161556599787.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201112/201112161557011065.png" alt="image" width="913" height="742" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;该图定义了本实例中的主要类的关系。&lt;/p&gt;&lt;p&gt;需要注意的是，参考资料中是作者实现了一个注入模块的编写，本实例重写了本部分，使用了UNITY来实现注入。&lt;/p&gt;&lt;p&gt;代码下载：&lt;a href="http://files.cnblogs.com/luminji/SmartCA.rar"&gt;SmartCA.rar&lt;/a&gt;&lt;/p&gt;&lt;p&gt;参考资料：《领域驱动设计C# 2008实现》&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2290313.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2011/12/16/2290313.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2011/12/15/2289062.html</id><title type="text">Unity TIP4: 带泛型参数的接口注入（interface,generic）</title><summary type="text">一：第一类需求接口和类定义如下： public interface ISample&lt;T&gt; {} public class Sample : ISample&lt;SomeClass2&gt; {} public class SomeClass2{}要求用Unity注入。1：运行时注入如果是运行时注入，可如下编码： using (IUnityContainer container = new UnityContainer()) { ConfigurationManager.Get...</summary><published>2011-12-15T08:25:00Z</published><updated>2011-12-15T08:25:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2011/12/15/2289062.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2011/12/15/2289062.html"/><content type="html">&lt;p&gt;&lt;strong&gt;一：第一类需求&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;接口和类定义如下：&lt;/p&gt;    public interface ISample&amp;lt;T&amp;gt; &lt;br/&gt;    {}&lt;br/&gt;    public class Sample : ISample&amp;lt;SomeClass2&amp;gt;&lt;br/&gt;    {}&lt;br/&gt;    public class SomeClass2{}&lt;p&gt;要求用Unity注入。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;1：运行时注入&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果是运行时注入，可如下编码：&lt;/p&gt;                using (IUnityContainer container = new UnityContainer())&lt;br/&gt;                {&lt;br/&gt;                    ConfigurationManager.GetSection("unity");&lt;br/&gt;                    UnityConfigurationSection.CurrentSection.Configure(container, "containerOne");&lt;br/&gt;                    container.RegisterType&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;, Sample&amp;gt;();&lt;br/&gt;                    var re = container.Resolve&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;&amp;gt;();&lt;br/&gt;                }&lt;p&gt;&lt;strong&gt;2：配置文件配置注入&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果是在配置文件中配置，配置文件如下：&lt;/p&gt;  &amp;lt;unity xmlns="http://schemas.microsoft.com/practices/2010/unity"&amp;gt;&lt;br/&gt;    &amp;lt;alias alias="ISample`1" type="ISample`1" /&amp;gt;&lt;br/&gt;    &amp;lt;alias alias="Sample" type="Sample" /&amp;gt;&lt;br/&gt;    &amp;lt;container name="containerOne"&amp;gt;&lt;br/&gt;      &amp;lt;register type="ISample`1" name="Sample" mapTo="Sample" /&amp;gt;&lt;br/&gt;    &amp;lt;/container&amp;gt;&lt;br/&gt;  &amp;lt;/unity&amp;gt;&lt;p&gt;编码如下：&lt;/p&gt;                using (IUnityContainer container = new UnityContainer())&lt;br/&gt;                {&lt;br/&gt;                    ConfigurationManager.GetSection("unity");&lt;br/&gt;                    UnityConfigurationSection.CurrentSection.Configure(container, "containerOne");&lt;br/&gt;                    var re = container.Resolve&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;&amp;gt;("Sample");&lt;br/&gt;                }&lt;p&gt;&lt;strong&gt;二：第二类需求&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;接口和类定义如下：&lt;/p&gt;    public interface ISample&amp;lt;T&amp;gt; &lt;br/&gt;    {}&lt;br/&gt;    public class Sample&amp;lt;T&amp;gt; : ISample&amp;lt;T&amp;gt;&lt;br/&gt;    { }&lt;br/&gt;    public class SomeClass2{}&lt;p&gt;&lt;strong&gt;1：运行时注入&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果是运行时注入，可如下编码：&lt;/p&gt;                using (IUnityContainer container = new UnityContainer())&lt;br/&gt;                {&lt;br/&gt;                    ConfigurationManager.GetSection("unity");&lt;br/&gt;                    UnityConfigurationSection.CurrentSection.Configure(container, "containerOne");&lt;br/&gt;                    container.RegisterType&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;, Sample&amp;lt;SomeClass2&amp;gt;&amp;gt;();&lt;br/&gt;                    var re = container.Resolve&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;&amp;gt;();&lt;br/&gt;                }&lt;p&gt;&lt;strong&gt;2：配置文件配置注入&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;如果是在配置文件中配置，配置文件如下：&lt;/p&gt;  &amp;lt;unity xmlns="http://schemas.microsoft.com/practices/2010/unity"&amp;gt;&lt;br/&gt;    &amp;lt;alias alias="ISample`1" type="ISample`1" /&amp;gt;&lt;br/&gt;    &amp;lt;alias alias="Sample`1" type="Sample`1" /&amp;gt;&lt;br/&gt;    &amp;lt;container name="containerOne"&amp;gt;&lt;br/&gt;      &amp;lt;register type="ISample`1" name="Sample" mapTo="Sample`1" /&amp;gt;&lt;br/&gt;    &amp;lt;/container&amp;gt;&lt;br/&gt;  &amp;lt;/unity&amp;gt;&lt;p&gt;编码如下：&lt;/p&gt;                using (IUnityContainer container = new UnityContainer())&lt;br/&gt;                {&lt;br/&gt;                    ConfigurationManager.GetSection("unity");&lt;br/&gt;                    UnityConfigurationSection.CurrentSection.Configure(container, "containerOne");&lt;br/&gt;                    var re = container.Resolve&amp;lt;ISample&amp;lt;SomeClass2&amp;gt;&amp;gt;("Sample");&lt;br/&gt;                }&lt;p&gt;&lt;strong&gt;三：总结&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;可以看到，如果是运行时注入，那么Resolve方法中，必须指定泛型的类型。&lt;/p&gt;&lt;p&gt;如果是在配置文件中，为了表明这是个带泛型参数的接口或者类，则需要为其指定后缀&amp;ldquo;`&amp;rdquo;。&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2289062.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2011/12/15/2289062.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2011/11/03/2195704.html</id><title type="text">ASP.NET性能优化之分布式Session</title><summary type="text">如果我们正在使用Session，那么构建高性能可扩展的ASP.NET网站，就必须解决分布式Session的架构，因为单服务器的SESSION处理能力会很快出现性能瓶颈，这类问题也被称之为Session同步。微软有自己的分布式Session的解决方案，那就是SessionStateServer，我们可以参考：ASP.NET Session State Partitioning http://blog.maartenballiauw.be/post/2008/01/23/ASPNET-Session-State-Partitioning.aspxASP.NET load balancing and</summary><published>2011-11-03T00:56:00Z</published><updated>2011-11-03T00:56:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2011/11/03/2195704.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2011/11/03/2195704.html"/><content type="html">&lt;p&gt;如果我们正在使用Session，那么构建高性能可扩展的ASP.NET网站，就必须解决分布式Session的架构，因为单服务器的SESSION处理能力会很快出现性能瓶颈，这类问题也被称之为Session同步。微软有自己的分布式Session的解决方案，那就是SessionStateServer，我们可以参考：&lt;/p&gt;&lt;p&gt;ASP.NET Session State Partitioning&amp;nbsp; &lt;br /&gt;&lt;a href="http://blog.maartenballiauw.be/post/2008/01/23/ASPNET-Session-State-Partitioning.aspx"&gt;http://blog.maartenballiauw.be/post/2008/01/23/ASPNET-Session-State-Partitioning.aspx&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;ASP.NET load balancing and ASP.NET state server&amp;nbsp; &lt;br /&gt;&lt;a href="http://blog.maartenballiauw.be/post/2007/11/ASPNET-load-balancing-and-ASPNET-state-server-(aspnet_state).aspx"&gt;http://blog.maartenballiauw.be/post/2007/11/ASPNET-load-balancing-and-ASPNET-state-server-(aspnet_state).aspx&lt;/a&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;不过本文是要换一个方案，那就是使用Memcached来到达分布式SESSION的架构。Memcached作为分布式的缓存服务器已经被广泛应用在网站建设中。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;一：Session的机制&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;Session是针对用户的，我们也可以理解为是针对浏览器的。在浏览器首次访问ASP.NET网页的时候（网页没有关闭session功能），它会发送如下的HTTP头给客户端：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718303254.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718354220.png" alt="image" width="709" height="177" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;浏览器在收到上面的HTTP头后，会将这个唯一的SESSIONID保存在自己的COOKIE中（只要没有禁用COOKIE，本文不讨论禁用COOKIE的案例，可参考本博文&lt;a href="http://www.cnblogs.com/fish-li/archive/2011/07/31/2123191.html"&gt;http://www.cnblogs.com/fish-li/archive/2011/07/31/2123191.html&lt;/a&gt;，写的很NICE）。当浏览器再次请求服务器进行访问的时候，它会在请求HTTP头中加入如下的标识，我们可以看到，这个SESSIONID就是上面的SESSIONID：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718351330.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border-width: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718383890.png" alt="image" width="719" height="210" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;浏览器和服务器间就是通过这样一种机制来确保用户SESSION的。&lt;/p&gt;&lt;p&gt;如果客户端浏览器禁用了Cookie会怎么样，我们会发现每一次刷新浏览器Set-Cookie都是不同的，而发送请求头中也永远不会出现Cookie标识。这个时候，我们会发现Session失效了（当然，微软为了防止出现这种情况，允许我们在sessionState中设置cookieless="true"，用URL来传递sessionid）。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二：Memcached Providers&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;我使用的Memcached客户端是Memcached Providers，下载完毕后，你会发现Memcached Providers已经提供了对分布式Session的支持功能。如果你还不会使用Memcached Providers，请参考此文&lt;a href="http://www.cnblogs.com/luminji/archive/2011/08/17/2140804.html"&gt;Memcached Tip 1：使用Memcached Providers&lt;/a&gt;。Memcached Providers提供的示例是直接将SESSION存储在数据库，我们可以通过配置来将SESSION支持存储在分布式SESSION的内存中，即，将下文中的dbType由SQL修改为none。：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718399887.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718449816.png" alt="image" width="728" height="182" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;使用Memcached Providers提供的分布式Session没有任何特别之处，因为Memcached Providers提供的SessionStateProvider类型实现的是ASP.NET中的SessionStateStoreProviderBase这个抽象类，我们可以看到配置文件中指定了Session的处理类是SessionStateProvider，所以，ASP.NET在接受到客户端的请求后，会自觉滴使用SessionStateProvider来处理所有的SESSION，也正是这个类，完成了将SESSION读取和存储在Memcached中（如果设置了SQL，则会同步存储到SQLSERVER数据库）。&lt;/p&gt;&lt;p&gt;SESSION的设置和读取与传统没有任何区别，读：&lt;/p&gt;            Session["sname2"] = "sluminjxxi";&lt;br/&gt;            Session.Timeout = 2;&lt;p&gt;取：&lt;/p&gt;            Response.Write(Session["sname2"]);&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;三：为什么要配置SQL&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;传统的SESSION的缺点，在仅使用dbType为none配置的时候都会存在。如Memcached的内存到达上限的时候会怎么办？Memcached使用LRU淘汰算法（最久未使用），在这里我们不需要去细究这个算法在Memcached内部到底是什么样一个机制，我们只需要知道，在内存紧张的时候，即使SESSION时间未到，Memcached也有可能把它干掉。所以，保险的做法是，在Memcached之下，再加上SQLSERVER的持久化保存。如果缓存命中的，直接取缓存，如果缓存没命中的，则再到数据库中确认一次。当然，这样会带来一些性能损耗，但是却是更安全的做法。&lt;/p&gt;&lt;p&gt;Memcached Providers提供的下载文件中，提供了初始化SESSION的一些脚本，正确执行后，它会生成如下一个表tblSessions，及若干存储过程：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718458147.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718457765.png" alt="image" width="246" height="297" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;tblSessions保存的是就是单独的Session，如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718462998.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718483324.png" alt="image" width="440" height="73" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;四：Memcached Providers的一个BUG&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;在当前的Memcached Providers（1.2版本）中关于SessionStateProvider（29520-TRUNK）是有一个BUG（我已提交到codeplex，相信他们的下一个版本应该能得到修正）的。如果我们测试SESSION失效时间，发现只要经过一次刷新后，就永远是20分钟（即默认）。这源于在ReleaseItemExclusive这个重载方法中（该方法用于释放对会话数据存储区中项的锁定），对于Session的重新存储没有加上过期时间，如下：&lt;/p&gt;&lt;p&gt;&lt;a href="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718497369.png"&gt;&lt;img style="background-image: none; padding-left: 0px; padding-right: 0px; display: inline; padding-top: 0px; border: 0px;" title="image" src="http://images.cnblogs.com/cnblogs_com/luminji/201109/201109291718529057.png" alt="image" width="524" height="248" border="0" /&gt;&lt;/a&gt;&lt;/p&gt;&lt;p&gt;注释掉的是Memcached Providers提供的源码，而正确的应该是我修正过的上一条。使用修正过的DLL，一切圆满了。&lt;/p&gt;&lt;p&gt;&amp;nbsp;&lt;/p&gt;&lt;p&gt;&lt;strong&gt;五：采用数据库存储SESSION的可扩展问题&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;随着访问量的进一步上升（当然，到了这种程度，说明网站做的很很成功，绝大部分的网站是不需要考虑这一步的），即便我们使用了Memcached作缓存，使用单一的SQLSERVER存储SESSION仍旧带来了性能问题，在这种情况下，我们对于数据库的设计可以采用水平分区的架构，即根据某种算法（可以根据SESSIONID，或者用户名等）将SESSION存储到不同的数据库中。这个时候，如果我们仍旧使用Memcached Providers，那么必须进一步修改源码了，由原先支持单一SQLSERVER服务器，编程支持多个服务器。当然，如果不喜欢SQLSERVER，还可以修改为支持mysql、mongodb、任何自定义的KEY-VALUE框架等等，此为后话，暂且不表。&lt;/p&gt;&lt;p&gt;本系列的前篇：&lt;/p&gt;&lt;p&gt;1：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/08/2169955.html"&gt;ASP.NET性能优化之构建自定义文件缓存&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/13/2172737.html"&gt;ASP.NET性能优化之让浏览器缓存动态网页&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/14/2174751.html"&gt;ASP.NET性能优化之减少请求&lt;/a&gt;&lt;/p&gt;&lt;p&gt;4：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/16/2178759.html"&gt;ASP.NET性能优化之反向代理缓存&lt;/a&gt;&lt;/p&gt;&lt;p&gt;5：&lt;a id="ctl02_lnkTitle" href="http://www.cnblogs.com/luminji/archive/2011/10/19/2181531.html"&gt;ASP.NET性能优化之局部缓存&lt;/a&gt;&lt;/p&gt;&lt;p&gt;本文首发于&lt;a href="http://www.agilesharp.com/"&gt;AgileSharp-创业互推平台&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2195704.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2011/11/03/2195704.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry><entry><id>http://www.cnblogs.com/luminji/archive/2011/10/19/2181531.html</id><title type="text">ASP.NET性能优化之局部缓存</title><summary type="text">在网站的开发过程中，经常碰到的一类需求场景是：1：页面含热点新闻，热点新闻部分需要10分钟更新一次，而整个页面的其它部分1天内都不会变动；2：首页的某个BANNER需要显式：欢迎***；上面场景中的1，如果整个页面的缓存失效都定为10分钟，则势必增加性能开销，所以最好的策略是页面的不同部分采用不同的缓存失效时长。对于场景2也一样，我们不应该为了迁就某个BANNER不能应用缓存，就让整个页面都不支持缓存。可以说，如果我们在开发网站过程中的缓存策略是不支持页面局部缓存的，整个架构就是不合理的。一：局部缓存常用解决方案针对上面的需求，有几类解决方案：1、Client Side Includes(CS</summary><published>2011-10-19T01:27:00Z</published><updated>2011-10-19T01:27:00Z</updated><author><name>Luminji</name><uri>http://www.cnblogs.com/luminji/</uri></author><link rel="alternate" href="http://www.cnblogs.com/luminji/archive/2011/10/19/2181531.html"/><link rel="alternate" type="text/html" href="http://www.cnblogs.com/luminji/archive/2011/10/19/2181531.html"/><content type="html">&lt;p&gt;在网站的开发过程中，经常碰到的一类需求场景是：&lt;/p&gt;&lt;p&gt;1：页面含热点新闻，热点新闻部分需要10分钟更新一次，而整个页面的其它部分1天内都不会变动；&lt;/p&gt;&lt;p&gt;2：首页的某个BANNER需要显式：欢迎***；&lt;/p&gt;&lt;p&gt;上面场景中的1，如果整个页面的缓存失效都定为10分钟，则势必增加性能开销，所以最好的策略是页面的不同部分采用不同的缓存失效时长。对于场景2也一样，我们不应该为了迁就某个BANNER不能应用缓存，就让整个页面都不支持缓存。&lt;/p&gt;&lt;p&gt;可以说，如果我们在开发网站过程中的缓存策略是不支持页面局部缓存的，整个架构就是不合理的。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;一：局部缓存常用解决方案&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;针对上面的需求，有几类解决方案：&lt;/p&gt;&lt;p&gt;1、Client Side Includes(CSI)：通过frame、iframe、 javascript、javacript+ajax等方式将另外一个页面的内容动态包含进来。像现在流行的jquery等javascript库对此有较好的支持。&lt;/p&gt;&lt;p&gt;优点：能够利用浏览器客户端并行处理及装载的机制；通过浏览器缓存机制可以降低网络传输时间，提高性能；计算放在客户端，能够降低服务器端压力&lt;/p&gt;&lt;p&gt;缺点：搜索引擎优化问题；javascript兼容性问题；客户端缓存可能导致服务器端内容更新后不能及时生效；XSS等安全隐患&lt;/p&gt;&lt;p&gt;2、Server Side Includes(SSI)：&lt;/p&gt;&lt;p&gt;优点：SSI技术是通用技术，不受具体语言限制，只需要Web服务器或应用服务器支持即可，Ngnix、Apache、Tomcat、Jboss等对此都有较好的支持&lt;/p&gt;&lt;p&gt;缺点：SSI在语法上不能够直接包含其他服务器的url（当然也可以通过redirect等来变通实现），因此在需要充分利用缓存及负载均衡的环境下相对不是很灵活。&lt;/p&gt;&lt;p&gt;当然如果不使用单独的缓存服务器，而是使用Ngnix，利用Ngnix对SSI及Memcached支持，通过NginxHttpSsiModule、 NginxHttpMemcachedModule也可以实现页面缓存，但与专业的缓存服务器（例如Varnish）相比较，Ngnix作为缓存服务器只适合于中小规模的场合。&lt;/p&gt;&lt;p&gt;3、使用ASP.NET的片段缓存&lt;/p&gt;&lt;p&gt;可以利用用户控件将页面分段，在ascx文件中写入缓存的语句，而不在aspx文件中写缓存语句，这样ASP.NET就可以只缓存ascx片断的输出了。&lt;/p&gt;&lt;p&gt;缺点：片段缓存不支持Location特性；缓存页面片段惟一合法的地方是web服务器。这是因为片段缓存在ASP.NET中是新的功能，所以浏览器和代理服务器不支持。由于它不是W3C标准，像SQUID和VARNISH这样的代理服务器也不支持它。&lt;/p&gt;&lt;p&gt;4、Edge Side Includes (ESI)：&lt;/p&gt;&lt;p&gt;Edge Side Includes(&lt;strong&gt;ESI&lt;/strong&gt;) 和Server Side Includes(&lt;strong&gt;SSI&lt;/strong&gt;) 和功能类似。SSI需要特殊的文件后缀(shtml,inc)。ESI可以直接通过URI包含远程服务器文件，ESI更适合用于缓存服务器上，缓存整个页面或页面片段，因此ESI特别适合用于缓存。本文要介绍的就是ESI的方式来支持局部缓存。&lt;/p&gt;&lt;p&gt;优点：ESI是一个W3C标准，被当下流行的缓存服务器SQUID，Varnish支持。&lt;/p&gt;&lt;p&gt;&lt;strong&gt;二：ESI的ASP.NET实现&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;本文所要阐述的是ESI局部缓存的实现。主页面（test1.aspx）前台：&lt;/p&gt;&amp;lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="test1.aspx.cs" Inherits="WebApplication2.aspx.test1" %&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;%@ Import Namespace="System.Globalization" %&amp;gt;&lt;br/&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;br/&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml"&amp;gt;&lt;br/&gt;&amp;lt;head runat="server"&amp;gt;&lt;br/&gt;    &amp;lt;title&amp;gt;&amp;lt;/title&amp;gt;&lt;br/&gt;&amp;lt;/head&amp;gt;&lt;br/&gt;&amp;lt;body&amp;gt;&lt;br/&gt;    &amp;lt;div&amp;gt;这里是局部缓存&amp;lt;/div&amp;gt;&lt;br/&gt;    &amp;lt;esi:include src="test2.aspx"/&amp;gt;&lt;br/&gt;    &amp;lt;div&amp;gt;局部缓存结束&amp;lt;/div&amp;gt;&lt;br/&gt;    &amp;lt;%=DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo)%&amp;gt;&lt;br/&gt;&amp;lt;/body&amp;gt;&lt;br/&gt;&amp;lt;/html&amp;gt;&lt;p&gt;主页面的后台请参看上篇，对主页面采取了缓存策略，即在页面中使用esi:include标识。&lt;/p&gt;&lt;p&gt;被包含的页面（test2.aspx）的前台：&lt;/p&gt;&amp;lt;%@ Page Language="C#" AutoEventWireup="true" CodeBehind="test2.aspx.cs" Inherits="WebApplication2.aspx.test2" %&amp;gt;&lt;br/&gt;&lt;br/&gt;&amp;lt;%@ Import Namespace="System.Globalization" %&amp;gt;&lt;br/&gt;&amp;lt;div&amp;gt;&lt;br/&gt;    局部缓存中的页面：&lt;br/&gt;    &amp;lt;%=DateTime.Now.ToString("U", DateTimeFormatInfo.InvariantInfo)%&amp;gt;&lt;br/&gt;&amp;lt;/div&amp;gt;&lt;p&gt;被包含的页面的后台什么也不需要处理，也就是不为它加入任何缓存策略，该页面是实时的。&lt;/p&gt;&lt;p&gt;VARNISH配置文件如下：&lt;/p&gt;backend default {&lt;br/&gt;     .host = "192.168.0.77";&lt;br/&gt;     .port = "80";&lt;br/&gt;}&lt;br/&gt;sub vcl_fetch {&lt;br/&gt;remove beresp.http.Set-Cookie;&lt;br/&gt;if(req.url ~ "test1.aspx") {&lt;br/&gt;        esi;&lt;br/&gt;    }&lt;br/&gt;if(req.url ~ "test2.aspx"){&lt;br/&gt;        return (pass);&lt;br/&gt;    }&lt;br/&gt;}&lt;br/&gt;sub vcl_recv {&lt;br/&gt;remove req.http.Cookie;&lt;br/&gt;#remove req.http.Accept-Encoding;&lt;br/&gt;    #remove req.http.Vary;&lt;br/&gt;}&lt;br/&gt;sub vcl_hit {&lt;br/&gt;    if(req.http.Cache-Control~"no-cache"||req.http.Cache-Control~"max-age=0"||req.http.Pragma~"no-cache"){&lt;br/&gt;        set obj.ttl=0s;&lt;br/&gt;        return (restart);&lt;br/&gt;    }&lt;br/&gt;    return (deliver);&lt;br/&gt;}&lt;br/&gt;sub vcl_deliver {&lt;br/&gt;if (obj.hits &amp;gt; 0) {&lt;br/&gt;set resp.http.X-Cache = "HIT";&lt;br/&gt;} else {&lt;br/&gt;set resp.http.X-Cache = "MISS";&lt;br/&gt;}&lt;br/&gt;}&lt;p&gt;上文的vcl_fetch函数中加了两个判断，指的是：如果碰到test1.aspx就处理esi标识，如果碰到test2.aspx，就直接忽略让后台IIS处理。&lt;/p&gt;&lt;p&gt;值得注意的是，启动命令中加入了-p选项（这是一个varnish的小问题，请查阅参考，此处不表）：&lt;/p&gt;&lt;p&gt;varnishd -a :8011 -T :8088 -f c:/varnish/etc/default.vcl -p esi_syntax=0x1 -s file,c:/varnish/var/cache,100M&lt;/p&gt;&lt;p&gt;&lt;strong&gt;三：效果&lt;/strong&gt;&lt;/p&gt;&lt;p&gt;启动varnish后，我们发现，对于test2.aspx，由于我们使用了esi对其进行了包含，而test2.aspx又未进行缓存，所以在test1.aspx的缓存有效期内，随着每一次刷新，test1.aspx的内容没有变动，但是所包含的test2.aspx区域，会实时刷新。&lt;/p&gt;&lt;p&gt;参考（第一小节大部分来自参考文字）：&lt;/p&gt;&lt;p&gt;&lt;a href="https://www.varnish-cache.org/trac/ticket/352"&gt;https://www.varnish-cache.org/trac/ticket/352&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://cd34.com/blog/infrastructure/no-esi-processing-first-char-not/"&gt;http://cd34.com/blog/infrastructure/no-esi-processing-first-char-not/&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://hi.baidu.com/chuanliang2007/blog/item/075f67963e20f315d31b7035.html"&gt;http://hi.baidu.com/chuanliang2007/blog/item/075f67963e20f315d31b7035.html&lt;/a&gt;&lt;/p&gt;&lt;p&gt;&lt;a href="http://www.w3.org/TR/esi-lang"&gt;http://www.w3.org/TR/esi-lang&lt;/a&gt;&lt;/p&gt;&lt;p&gt;本系列的前篇：&lt;/p&gt;&lt;p&gt;1：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/08/2169955.html"&gt;ASP.NET性能优化之构建自定义文件缓存&lt;/a&gt;&lt;/p&gt;&lt;p&gt;2：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/13/2172737.html"&gt;ASP.NET性能优化之让浏览器缓存动态网页&lt;/a&gt;&lt;/p&gt;&lt;p&gt;3：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/14/2174751.html"&gt;ASP.NET性能优化之减少请求&lt;/a&gt;&lt;/p&gt;&lt;p&gt;4：&lt;a href="http://www.cnblogs.com/luminji/archive/2011/09/16/2178759.html"&gt;ASP.NET性能优化之反向代理缓存&lt;/a&gt;&lt;/p&gt;&lt;p&gt;本文首发于&lt;a href="http://www.agilesharp.com/"&gt;AgileSharp-创业互推平台&lt;/a&gt;&lt;/p&gt;&lt;img src="http://www.cnblogs.com/luminji/aggbug/2181531.html?type=1" width="1" height="1" alt=""/&gt;&lt;p&gt;&lt;a href="http://www.cnblogs.com/luminji/archive/2011/10/19/2181531.html" target="_blank"&gt;本文链接&lt;/a&gt;&lt;/p&gt;</content></entry></feed>
